• 集合框架


    集合框架Collection框架


     

    Collection接口

       |-----List接口(有序可重复)

           |------ArrayList类(底层采用变长数组实现,查找速度快,不安全,效率高)

           |------Vector类(底层采用变长数组实现,安全,效率低)

           |------LinkedList类(底层采用双向链表实现,插入删除速度快)

       |-----Set接口(无序不可重复,有序是指存储顺序和插入的顺序一样)

               |----HashSet类(无序不可重复,用hash表实现,底层是HashMap,屏蔽了value的Map)

                     |------LinkedHashSet类(有序不可重复,底层维护了hash表和双向链表)

               |----SortedSet接口(数据排序的,不可重复)

                     |------TreeSet类(对插入的数据排序,不可重复,底层采用二叉排序树实现,底层就是一个TreeMap)

    Map接口(无序,key不可重复,value可重复)

      |-----HashMap类(用hash表实现,无序,线程不安全,效率高,key值允许为null)

      |-----HashTable类(用hash表实现,无序,线程安全,效率低,key值不允许为null)

      |-----SortedMap接口(排好序的Map)

              |------TreeMap类(排好序的,对插入的key值进行排序,底层采用排序二叉树实现)

    Collection接口定义的方法


    Collection接口是所有集合的顶级父接口。Collection接口中定义的方法是每个集合都具备的!

    1. boolean add(Object o):向集合中添加新元素,返回值:true添加元素成功。

    2. boolean remove(Object o);将给定元素从集合中删除,返回值:true删除成功

    3. int size():返回当前集合的元素总数

    4. boolean isEmpty();判断集合中是否含有元素。

    5. boolean contains(Object obj);判断集合是否包含给定的元素

    6. void clear();清空集合;

    7. boolean addAll(Collection c);将给定集合中的所有元素添加到当前集合

    8. boolean removeAll(Collection c);删除当前集合中与给定集合相同的元素

    9. Iterator iterator();返回当前集合的迭代器

    10. Object[]  toArray();用于将集合转换为数组  T[ ]  toArray( T[ ] a)

    11. Arrays.asList()将数组转成list类型

    List集合


       List集合:有序且可重复集

        两个常用的实现类:ArrayList和LinkedList

        用法完全一样,只是因为实现方式不同,各有千秋

        ArrayList使用数组实现,所以更适合读取存储的数据

        LinkedList使用链表实现,所以更适合插入和删除元素。

        List接口中定义的独有方法

       (1)Object get(int index):获取给定索引出的元素

       (2)Object set(int index,Object obj);将给定的元素存入集合指定位置

       (3)add(int index ,Object obj);向集合指定位置插入元素

       (4)remove(int index);删除指定位置的元素

       (5)int indexOf(Object obj);查询指定元素在集合中的位置

            在集合中查询给定元素第一次出现的位置。

               这里也是使用给定元素与集合元素进行equals的比较方式

       (6)int  lastIndexOf(Object  obj);

            在集合中查询给定元素最后一次出现的位置。

       (7)List<E> subList(int from,int to)

    获取当前集合的部分内容。取子集

    凡是表示范围的都是包含开始,不包含结束。

    4、案例

       1List list = new ArrayList();

            ArrayList内部使用对象数组形式实现。

    在创建ArrayList对象时,ArrayList会初始化一个数组,

    当要存放的元素数量大于数组时,ArrayList会自动扩容数组长度。

            list.add("一");

    list.add("二");

    System.out.println(list);

    ArrayList重写了Object的toString方法

    返回的字符串格式为: [元素1.toString(),元素2.toString(),....]

    会顺序调用集合中每个元素的toString方法,并拼接在一起。

    list.clear();

    System.out.println(list==null);//false

    System.out.println(list.isEmpty());//true

    注意:判断null和isEmpty的区别

             null指的是集合对象是否存在。

          isEmpty()指的是集合对象是存在的,只不过没有元素。

       (2List list=new ArrayList();

                  Point p=new Point(1,2);

                  list.add(p);

                  list.add(p);

                  list.add(new Point(3,4));

                  Point point=new Point(1,2);

                  System.out.println(list.contains(point));

                  list.remove(point);

                  System.out.println(list);

    注意:元素的equals方法对集合的很多操作都有影响!

             判断集合是否包含给定的元素时,集合会将这个元素与集合中的元素分别进

          行equals比较,若有返回值为true的,则认为集合包含给定的元素。

          remove方法是将给定的元素与集合中每个元素进行equals比较,删除第一

          个比较结果为true的元素。

      (3 List list1 = new ArrayList();

                  List list2 = new ArrayList();

                  List list3 = new ArrayList();

                  list1.add("一");

                  list1.add("二");

                  list1.add("三");

                  System.out.println(list1);

                  list2.add("四");

                  list2.add("五");

                  list3.add("一");

                  list3.add("二");

                  list1.addAll(list2);

                  System.out.println(list1);

                  list1.removeAll(list3);

                  System.out.println(list1);

                  list1.retainAll(list2);

    取交集:只保留list1中和list2中相同的元素

                  System.out.println(list1);

            以上方法比较元素相同都是使用equals方法比较的。

     (4  List list = new ArrayList();

                  list.add("one");

                  list.add("two");

                  list.add("three");

                  for(int i = 0;i<list.size();i++){

                         String str = (String)list.get(i);

                         System.out.println(str);

                  }

                  set方法用于替换集合中指定位置上的元素,set方法的返回值为被替换的元素

                  set方法指定的索引位置不能大于数组的元素数量,否则会出现下标越界异常

                  Object old = list.set(2, "二");

                  System.out.println(list);

                  System.out.println("被替换的元素:"+old);

    (5String[] array = (String[])list.toArray(new String[0]);

         将集合转换为数组

         注意:要确保集合中存放的元素类型是一致的!

             且要转换的目标数组类型要与元素类型一致。

                   toArray()方法用于将集合转换为数组

                   toArray方法是Collection定义的方法。所有集合都具备 

                   想转换什么类型的数组,toArray方法参数就传什么类型数组的实例

                   我们给定的数组实例不需要给长度,因为不会使用,

                   toArray方法只是借鉴了我们传入参数数组的类型。

    (6)     List<Integer> list=new ArrayList<Integer>();

                  for(int i=0;i<10;i++){

                         list.add(i);

                  }

                  System.out.println(list);

                  List<Integer> subList=list.subList(0,4);

                  System.out.println(subList);

    修改子集会影响原集合中的元素

                  for(int i=0;i<subList.size();i++){

                         subList.set(i, subList.get(i)*10);

                  }

                  System.out.println(subList);

                  System.out.println(list);

    5、迭代器Iterator接口

        Collection中提供了一个方法

    Iterator iterator():该方法用于返回当前集合的迭代器

    迭代器是用于遍历集合的。

    由于每种集合的内部实现不同,迭代器遍历集合元素的方式也不尽相同,所以我们不需

    要记住每一种迭代器的名字。我们就统一把他们看成迭代器去使用就可以了。

    Iterator接口

    定义了三个方法

    boolean hashNext()::询问迭代器迭代的集合是否还有元素。

    Object next()::next方法获取集合中下一个元素与get方法一样,

                 存放的时候以Object存的,取的时候也是以Object返回的,所以要造型。

    void remove():删除刚刚迭代出来的元素。

    使用迭代器的流程

    问拿删。可以不删除元素。但一定要注意,问一次拿一次!

    List迭代

    List list = new ArrayList();

    list.add("one");

    list.add("#");

           list.add("two");

           list.add("#");

           list.add("three");

           list.add("#");

           java.util.Iterator:迭代器是专门为while循环设计的

                  Iterator it = list.iterator();

           while(it.hasNext()){

                  String element = (String)it.next();

                  if("#".equals(element)){

                         //将上面通过next方法获取的元素从集合中删除

                         it.remove();

                         迭代器在迭代过程中,不能通过使用集合定义的删除方法去删除集合元

                素,一定要使用迭代器的删除方法,否则迭代过程中会产生异常!

    //list.remove(element);

                  }           

    System.out.println(element);

           }

       如下迭代:对于链表性能低下(每次找元素都是从第一个开始往后找)

             没有迭代器性能好!迭代器是集合的最优遍历算法

        for(int i=0;i<eggs.size();i++){

                  System.out.println(eggs.get(i)) ;

        }  

    6、泛型

    java1.5之后支持的一个新特性

    可以在我们使用某个类的时候,动态的给该类的属性或方法的参数,返回值指定类型。

    public class ArrayList<E>{

       public boolean add(E e){...}

       public E get(int index){...}

    }

    List<String> list = new ArrayList<String>();

    list.add("123");

    String s =list.get(0);

    当支持泛型的类我们在使用的时候不指定泛型类型时,那么泛型类型默认就是Object

    集合对泛型的支持

    集合中的泛型指定的是存放的元素是什么类型的。

                  List<String> list = new ArrayList<String>();

           list.add("123");

           list.add("456");

           list.add("789");

    //list.add(123);//参数类型不匹配!

           for(int i =0;i<list.size();i++){

                  get方法获取元素时直接是泛型指定的类型,无需再进行造型了

                  String element = list.get(i);

                  System.out.println(element);

           }

           迭代器也支持泛型

           但要注意!迭代器指定的泛型类型一定要和遍历的集合的泛型类型一致!

                  Iterator<String> it = list.iterator();

           while(it.hasNext()){

                  使用迭代器获取元素时也不再需要造型

                  String element = it.next();

                  System.out.println(element);

           }

    自定义泛型

    泛型的语法

    在定义类的时候,在类名之后用<>定义泛型

    泛型命名可以是字母与数字的组合,数字不能是第一个字符

    若指定多个泛型,中间用","分开

    class Point<X,Y>{

                  private X x;

                  private Y y;

                  public Point(X x, Y y) {

                         this.x = x;

                         this.y = y;

                  }

                  public X getX() {

                         return x;

                  }

                  public void setX(X x) {

                         this.x = x;

                  }

                  public Y getY() {

                         return y;

                  }

                  public void setY(Y y) {

                         this.y = y;

                  }

    泛型是一个动态的过程,是用于告知jvm运行时该类的属性类型。

    所以,不指定泛型时,默认就是Object

                  public static void dosome(Point p){

                         p.setX("12");

                         p.setY("12.2");

                         System.out.println(p);

                  }

           }

    public static void main(String[] args) {

                  Point<Integer, Double> point1=

    new Point<Integer, Double>(1,1.2);

                  int x=point1.getX();

                  double y=point1.getY();

                  Point.dosome(point1);

    }

    JAVA泛型的? extends和? super的比较

       (1)? extends叫做向上造型.

        ArrayList<? extends BaseCls> list1 = new ArrayList<BaseCls>();

        意味着这个list1里面放的都是BaseClse的子类,保证你可以通过list.get(index)

        得到的类都是BaseCls或者BaseCls的子类.

        BaseCls cls = list1.get(0);//合法的

        list1.add(new BaseCls());或者list1.add(new CldCls());都是不合法的.

        这里面是不能通过add函数放东西进去的,那这样有什么用呢.

        一般来讲,定义成? extends BaseCls的参数通常只能用来从里面取数据.

        如下downbound方法可以接受? extends BaseCls类型的list

        public void downbound(List<? extends BaseCls> list) {  

            for(BaseCls cls:list){

                cls.func();

            }      

        }

        ArrayList<BaseCls> list1 = new ArrayList<BaseCls>();

        ArrayList<CldCls> list2 = new ArrayList<CldCls>();

        downbound(list1);和downbound(list2);都是合法的。

      (2)? super BaseCls叫做向下造型.

           ArrayList<? super BaseCls> list2 = new ArrayList<BaseCls>();

           意味着这个list2里面只能放BaseClse或者它的子类.

           list2.add(new BaseCls());或者list2.add(new CldCls());都是合法的.

           list2.get(index)返回的是Object类型,因为不知道是那个具体类.

           限制了放进去的对象的类型

           public void upperbound(List<? super BaseCls> list) {

                   list.add(new BaseCls()

                   list.add(new CldCls());

           }

    7、增强for循环

    java1.5后的有一个新特性

    增强for循环 又叫新循环

    新循环的作用是遍历集合和数组的。不能用新循环代替传统循环。

    for(TYPE ele : array){

    ....

    }

    TYPE:集合或数组的元素类型

    ele:元素的引用变量

    array:要遍历的集合或数组的实例

    新循环的循环次数有遍历的集合或数组的长度决定。

    每次循环时,会将集合或数组中的元素依次赋值给ele,然后进入循环体。

     使用新循环注意两点

    (1)新循环是在编译时动态将新循环转化为迭代器方式遍历

    (2)因为新循环使用迭代器方式遍历,所以在遍历集合时,不能通过集合删除元素。

    java1.5后出现的特性有:泛型  增强for循环  包装类的自动拆装箱

    8、队列Queue接口(extends Collection

    队列是一种非常常用的数据结构,存取数据本着先进先出原则。

    java中提供了Queue接口来描述队列。

    常用的实现类为LinkedList。

    boolean offer(E e):向队列末尾追加元素,追加成功返回true

    E poll():从队首获取元素。注意:获取后,队首元素就从队列中删除了。

            如果队列为空时,返回null。

    E peek():获取队首元素,但该元素不会从队列中删除。

    9、栈结构Deque接口

    Deque接口

    栈也可以保存一组数据,在存取方式上也有要求,本着先进后出原则。

    LinkedList也是其一个实现类

    push(E e):向栈顶压入新元素

    E pop():返回栈顶元素,并从栈中删除。

           当栈中没有元素时,调用该方法会引发异常

    E peek():获取栈顶元素,但不删除

    遍历栈结构:

       (1)Iterator<String> it=deque.iterator();

                  while(it.hasNext()){

                         String str=it.next();

                         System.out.println(str);

                  }

       (2)while(deque.peek() != null){

                         String element = deque.pop();

                         System.out.println(element);

                  }

    10、CollectionCollections的区别?

      Collection 抽象的集合概念,实现它的有List和Set。

      Collections 集合静态工具类, 包含集合的工具方法,如

        sort、reverseshuffle、binarySearch等。

    11Comparable接口和Comparator接口

    集合的排序

    若要进行排序,就要确定元素的大小。对象与对象间的大小关系如何确定?

       (1)java提供了一个接口Comparable

    Comparable接口定义了一个抽象方法compareTo()

    compareTo():用于定义对象间的比较规则。

    当我们定义的一个类若实现了该接口,那么就说明这个类的实例是可比较的。

    String类实现了Complarable接口,String对象时可以比较的。、

    定义Point类可以比较

    public class Point implements Comparable<Point>{

           private int x;

           private int y;

           public Point(int x,int y){

                  this.x = x;

                  this.y = y;

           }

           返回的int值不关心具体的值是多少,只关心取值范围

           当返回值大于0 表示当前对象比参数对象大(当前对象往右移)

           当返回值小于0 表示当前对象比参数对象小(当前对象往左移)

           当返回值等于0 表示当前对象和参数对象相等

           public int compareTo(Point o) {

                  int r = this.x * this.x + this.y * this.y;

                  int r1 = o.x * o.x + o.y * o.y;

                  return r-r1;//升序

           }

    }

    public static void main(String[] args) {

                         List<Point> list=new ArrayList<Point>();

                         list.add(new Point(3,2));

                         list.add(new Point(5,2));

                         list.add(new Point(1,6));

    注意:使用Collections的sort()方法,集合里的元素必须是可比较的

                         Collections.sort(list);

                         System.out.println(list);

    }

       (2)当类中的比较规则不能满足我们对排序的要求时,可以使用Collections的重载sort

            方法。给定一个比较规则,按照我们的规则比较后进行自然排序。

    Collections.sort(Collection c,Comparator cc)

    Comparator接口:用于定义一种比较规则。

            通常Comparator不需要定义额外的子类实现,都是使用匿名类的方式创建实例。

    List<String> list = new ArrayList<String>();

           list.add("mary");

           list.add("Killer");

           list.add("able");

           字符串实现了Comparable接口,所以字符串本身是具备可比较的

           Collections.sort(list);

           System.out.println(list);

           需求:按照字符串的长短来排序

           字符串的比较规则不能满足这个排序需求时,

        我们可以额外的定义比较规则来满足该排序的需求

           Comparator<String> comparator =

                         new Comparator<String>(){

                                public int compare(String o1, String o2) {                               

                                       return o1.length() - o2.length();

                                }           

                         };

                 

           Collections.sort(list, comparator);

           System.out.println(list);

    12Set集合

     Set集有别于List集最大的特点的是不存放相同的元素。Set集合不能通过索引的形式获取元素。

    常见的实现类

    HashSet:使用散列算法实现的Set集合

    TreeSet:使用二叉树算法实现的Set集合(底层就是TreeMap,key是存放的数据,value为null。底层都是调用TreeMap的方法来实现的)

    遍历Set集合的元素只有一种方式,迭代器。Set集合不支持索引,也不具备List集合的get方法。

    (1)遍历Set集合中的元素

     Set<String> set = new HashSet<String>();       

            无序是指元素存放的顺序与取出来的顺序不一致

     但是在元素不修改的前提下,如论以什么顺序存放在Set集合中的顺序都是一定的。

            set.add("two");

            set.add("three");

            set.add("one");

            //迭代器

            Iterator<String> it = set.iterator();

            while(it.hasNext()){

                  String str = it.next();

                  System.out.println(str);

            }

             增强for循环同样可以遍历Set集合, 对于编译器而言,增强for循环在编译后会转换为iterator,所以可以用增强循环遍历

                  for(String str : set){

                         System.out.println(str);

                  }

    (2)随机生成20个不重复的数字:

     Set<Integer> set = new HashSet<Integer>();

     Random r = new Random();

     while(size<20){

    set.add(r.nextInt(100));

     }

        hashcode方法与HashSet之间的关系

        HashSet在存放某个元素时,会先获取该元素的hashcode值,然后进行一系列的运算,之后确定将元素存放在什么位置。这里通过的算法确定位置,算法就是“散列算法”。

    可以看出,一个元素要想存入HashSet需要依赖hashcode()方法的返回值。

    hashcode()方法是Object定义的方法,所有类都具有该方法。我们应该妥善的重写hashcode()方法。

    javaAPI中对重写该方法有说明:

    若我们重写了equals()方法,就应该重写hashcode()方法。

    hashcode()方法应与equals()方法返回一致。

    即:当两个对象equals()方法返回true时,hashcode的返回值应该相同。

    在对象没有修改的前提下,多次调用hashcode方法返回的数字不应该发生改变。

    当两个对象的equals方法返回false时,hashcode值不是必须不同的。

    根据HashSet实现原理可以看出,若不同对象的hashcode值都相同,那么使用HashSet的效率会大大的降低。

    13、Map接口

     (1)Map是一种以键值(key-value)对的形式存放数据的结构。

          Map的存储要求是,key不能重复。

      散列表中元素的顺序是散列数组中元素的顺序,与散列算法的计算结果有关,与添加顺序无关称为“无序集合”。无序!=随机 

     (2)最常用实现类

          HashMap:以散列算法实现的Map

          TreeMap:以二叉树算法实现的Map

      TreeMap是添加元素时会按照某种排序规则来寻找存放的位置,

    如何排序:先看有没有传入比较器TreeMap tm = new TreeMap(new MyComparator());

      有的话按照给的比较器来排序,没有的话根据TreeMap中的key自身的比较规则来排序(Comparable),这时key值如果是不可比较的,则报错。

     (3)HashSet实际上使用HashMap实现的。

          当我们将一个元素add到HashSet中时,HashSet将这个元素作为key存入了HashMap,value值设置为null。因为HashMap对key的要求是不重复且存放顺序与获取顺序不同,正好满足Set集合的特征。

      HashMap要求Key对象在hashCode值不发生改变的情况下,只能保存一次。

     (4)HashMap的性能

      HsahMap散列表算法,面向查找优化的算法,查找性能优异(无论多少数据,查找次数少于3次)

          Capacity 散列表容量:HashMap的散列数组的大小(能存储key-value数量的最大值)。

      Initial capacity 初始容量:创建HashMap实例时,默认创建的散列数组的大小。默认为16。可以改变。但一般不会这样做。

      Size 大小: HashMap中存储的数据总数。存储(key-value)的数量。

      散列桶:散列值相同的元素集合(散列桶长度越小,性能越好)

              size/容量<=加载因子(75%)

      load factor 加载因子:加载因子的值默认为0.75。这是一个比值,size/capacity。当size与capacity的比值大于0.75时,hashmap会对散列数组进行扩容,并重新分配内部元素位置(重新散列rehash)。rehash是需要消耗一定的性能的。应减少rehash的次数来提高性能。

     性能优化

          影响性能:散列表容量和加载因子

      加载因子较小时候散列查找性能会提高,同时也浪费了散列桶空间容量。

      0.75是性能和空间相对平衡结果。在创建散列表时候指定合理容量,减少rehash提高性能。

     (5)Map的存取元素的方法

      V put(K k,V v):将key-value对存入Map。若Key值已经在Map中存在的话,那么就将Value值替换。返回值则是被替换的Value,若该Key不存在,则将Value存入,返回值为null。

      V get(Object k):根据给定的key值获取对应的Value。若当前给定的key在Map中不存在,则返回null。

      boolean containsKey(Object key):查看当前Map中是否包含给定的key,包含返回true。

      boolean containsValue(Object value):查看当前Map中是否包含给定的Value,包含返回true。

    (6)遍历Map

     遍历Map有三种方式:

         a、遍历Map中所有的key

            public Set keySet():调用keySet()方法会返回一个Set集合的实例,其中保存

         的元素为Map中的所有key。

     b、遍历Map中所有的键值对(Entry)

    public Set entrySet():调用entrySet()方法会返回一个Set集合的实例,其中保存的元素为Map中的每一组键值对,每个键值对用一个Entry实例保存。

     c、遍历Map中所有的value(不常用)

    案例:

    (1)统计str字符串中每组数字出现的次数

     思路:

      先将字符串按照","拆分,将每组数字作为key,将出现次数作为value存入map。这样每当统计一组数字时,我们只需要看这组数字作为key是否在map中存在,不存在则是第一次统计。若存在,则将出现次数累加即可。

    String str = "123,456,778,908,123,454,678,234,908,123";

    String[] array = str.split(",");

    Map<String,Integer> map = new HashMap<String,Integer>();

    for(String sub : array){

           if(map.containsKey(sub)){

                  map.put(sub, map.get(sub) + 1);         

           }else{

                  map.put(sub, 1);

                               

           }

    }

    System.out.println(map);

    (2)遍历Map中的所有Key

                        Map<String, Integer> map=new HashMap<String, Integer>();

                map.put("A", 1);

                map.put("C", 3);

                map.put("B", 2);

                Set<String> set=map.keySet();

                for(String key:set){

                    Integer value=map.get(key);

                    System.out.println(key+","+value);

        }

    (3)遍历Map的键值对

         Map中有一个内部类Entry,其每一个实例描述一组键值对。当我们调用put方法存放数据时,Map会创建一个Entry实例,并将key,value存入该对象后保存到Map中。所以,散列数组中,每一项的LinkedList中保存的都是Entry。

                     Map<String, Integer> map=new HashMap<String, Integer>();

                map.put("A", 1);

                map.put("C", 3);

                map.put("B", 2);

                Set<Entry<String, Integer>> set=map.entrySet();

                for(Entry<String, Integer> entry:set){

                    String key=entry.getKey();

                    Integer value=entry.getValue();

                    System.out.println(key+":"+value);

            }

    (4)遍历Map中的所有Value

                     Map<String, Integer> map=new HashMap<String, Integer>();

                map.put("A", 1);

                map.put("C", 3);

                map.put("B", 2);

     为什么返回Collection而不返回Set?

     因为Set集合不能存放重复元素,而Map中value是可以重复的,若返回为Set集合,可能会丢失信息。

                Collection<Integer> set=map.values();

                for(Integer integer:set){

                    System.out.println(integer);

    }

  • 相关阅读:
    svn随笔
    关于PHP调用IE的Com组件的一些要求
    Bash总结
    Firefox常用扩展
    proftpd的一些简单配置
    lua积累
    backbone.js源码解析:extend、Backbone.View
    Ubuntu 12.04LTS 安装VMwareWorkstation
    关于安装Ubuntu不将mbr写入grub的经验
    .NET6发布到linux
  • 原文地址:https://www.cnblogs.com/qin-derella/p/6628008.html
Copyright © 2020-2023  润新知