java集合Collection、List、Set详解

java高级之集合

概述

  • 万事万物皆对象,我们可以使用数组来存储对象但存在一些弊端。
  • 集合就像是一种java容器,可以动态的存储多个对象,可以把它理解为是动态的数组。
  • 集合框架包含三大块内容:对外的接口、接口的实现和对集合运算的算法。

集合 VS 数组

数组

  • 数组存储的数据有序、可重复;
  • 数组初始化以后,长度确定,不可变,不易扩展;
  • 数组声明的类型,元素初始化时的类型便确定;
  • 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,效率不高;无法直接获取存储元素的个数。

集合

  • 长度可变;
  • 可以存储不同的数据类型。

整体概述

  • Collection接口,理解为单列集合,存储对象时以单个来存储。

  • Map接口:理解为双列集合,用来存储一对(key - value)一对的数据。

    image-20200815225658034

Collection接口

常用方法

  • Collection接口常用方法总结。
方法返回值 方法名 方法的描述
boolean add(Object e) 添加单个元素
boolean addAll(Collection c) 指定集合的元素添加到此集合
int size() 获取集合中的元素个数。
void clear() 清空集合
boolean isEmpty() 判断该集合是否为空
boolean contains(Object o) 判断是否包含该元素
boolean containsAll(Collection c) 判断是否包含指定集合
boolean remove(Object o) 通过equals()方法找到指定的第一个元素并删除
boolean removeAll(Collection<?> c) 删除指定集合在本集合中的元素
boolean retainAll(Collection<?> c) 取交集, c中存在的元素
boolean equals(Object o) 判断集合是否相等,元素都相同时才相等
int hashCode() 返回此集合的哈希码值。
Object[] toArray() 返回一个包含此集合中所有元素的数组。
Iterator<E> iterator() 返回迭代器对象
  • 代码试验如下:

    @Test
    public void test1(){
          
          
        Collection collection = new ArrayList();
        //1、add(Object e)  添加单个元素
        collection.add("AA");
        collection.add(new Student("张三",24));
        Collection collection1 = new ArrayList();
        collection1.add("BB");
        collection1.add("CC");
        //2、addAll(Collection c)  指定集合的元素添加到此集合
        collection.addAll(collection1);
        //3、size() 获取集合中的元素个数
        collection.size();
        //打印一下该集合
        System.out.println(collection);
        //4、clear() 清空集合
        //collection.clear();
        //5、isEmpty() 判断集合是否为空
        System.out.println("集合是否为空:"+collection.isEmpty());
        //6、boolean contains(Object o); 判断是否包含该元素
        System.out.println(collection.contains("AA"));
        //如果不重写equals()和hashcode()方法,会是false
        System.out.println(collection.contains(new Student("张三",24)));
        //7、boolean containsAll(Collection c)  判断是否包含指定集合
        System.out.println(collection.containsAll(collection1));
        //8、boolean remove(Object o),通过equals()方法找到指定的第一个元素并删除
        collection.remove("AA");
        //9、boolean removeAll(Collection coll) 删除指定集合在本集合中的元素
        collection.removeAll(Arrays.asList("BB","CC"));
        //打印一下该集合
        System.out.println(collection);
        collection.add("AA");
        collection.add("BB");
        collection.add("CC");
        //10、boolean retainAll(Collection c)  取交集
        collection.retainAll(Arrays.asList("BB","CC"));
        //打印一下该集合
        System.out.println(collection);
        //11、boolean equals(Object obj) 判断集合是否相等,元素都相同时才相等
        System.out.println(collection.equals(Arrays.asList("BB","CC")));
        //12、返回该集合的hashCode()
        System.out.println(collection.hashCode());
        //13、Object[] toArray(), 返回一个包含此集合中所有元素的数组。
        Object[] arr = collection.toArray();
        for (Object o : arr) {
          
          
            System.out.println(o);
        }
    }
    

迭代器

  • 设计模式中有一种模式叫做迭代器模式,它的定义为:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

  • 迭代器即Iterator对象,Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。

  • 集合调用iterator()方法,返回Iterator对象,该对象用于遍历集合,而集合对象每次调用该方法时,返回的是一个全新的迭代器。

迭代器中的方法

  • 迭代器中的方法

    方法的返回值 方法 方法的描述
    boolean hasNext() 判断迭代器是否有下一个元素
    E next() 返回迭代中的下一个元素。
    default void remove() 在底层集合中删除元素
  • 测试如下

     @Test
        public void testIterator(){
          
          
            Collection collection = new ArrayList();
            collection.add("AA");
            collection.add("BB");
            collection.add(123);
            collection.add(456);
            collection.add(new Student("Jerry",20));
            collection.add(false);
            //14、iterator() 返回此集合中的元素的迭代器
            //每次调用时都得到一个全新的迭代器对象,默认游标在集合的第一个元素之前。
            Iterator iterator =  collection.iterator();
            // 14.1  hasNext():判断该集合是否有下一个元素
            while(iterator.hasNext()){
          
          
                // 14.2 next():
                //      1、指针(游标)下移
                //      2、将游标下移以后集合位置上的元素返回
                System.out.println(iterator.next());
            }
            System.out.println("============================================");
            //得到该集合的迭代器对象
          Iterator iterator1 =  collection.iterator();
          while(iterator1.hasNext()){
          
          
              String b = (String)iterator1.next();
              if (b.equals("BB")) {
          
          
                  //14.3 remove() 删除底层集合中的该元素
                  //详情见下面的Arraylist 中iterator() 方法
                  iterator1.remove();
              }
              System.out.println("当前元素为:" + b);
          }
        }
    

源码简单分析

  • Arraylistiterator() 方法为例,简单分析如下:

    //Arraylist中的iterator()方法
    public Iterator<E> iterator() {
          
          
       return new Itr();
    }
    //私有内部类
    private class Itr implements Iterator<E> {
          
          
      			//游标,用于记录要返回的元素的索引
            int cursor;   
      			//返回的最后一个元素的索引;如果没有这个值就是-1 当执行remove()方法时,置为-1
            int lastRet = -1; 
      			//modCount 是集合的修改次数,当进行add或者remove时,modCount会+1;
      			//expectedModCount是指集合的迭代器的版本号,初始值是modCount;
      			//使用集合中的add或者remove方法时,expectedModCount不会变;
      			//使用迭代器的add或者remove方法时,会同步expectedModCount。
            int expectedModCount = modCount;
            Itr() {
          
          }
      			
            public boolean hasNext() {
          
          
                return cursor != size;
            }
    				//每次执行一次 cursor+1  返回元素
            @SuppressWarnings("unchecked")
            public E next() {
          
          
                checkForComodification();
                int i = cursor;
                if (i >= size)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;
                return (E) elementData[lastRet = i];
            }
    				//迭代器中的remove(), 使用ArrayList的remove()方法,
      			//但是会修正次数,expectedModCount = modCount;
      			//并且将 lastRet = -1,所以不能连续使用remove()方法。
            public void remove() {
          
          
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
                try {
          
          
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
          
          
                    throw new ConcurrentModificationException();
                }
            }
    
           	.....
    				//校验 错误的检测,通过比较:modCount != expectedModCount  
            //在使用集合的add()、remove()、clear()方法时,modCount++
            final void checkForComodification() {
          
          
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
    

循环

  • 数组的增强for循环使用的是普通的for 循环,在循环体中给数组赋值时数组会改变。

  • 集合使用的是迭代器的for循环。

    //for(集合元素的类型 局部变量 : 集合对象)
    //内部仍然调用了迭代器。
    for (Object o : collection) {
          
          
        System.out.println(o);
    }
    //编译器在看到一个实现了Interator接口的对象的增强for循环时,会自动地重写,变成使用迭代器来遍历集合。
    Iterator<Integer> iterator=collection.iterator();
    while(iterator.hasNext()){
          
          
        System.out.println(iterator.next());
    }
    

List接口

概述

  • List接口是Collection接口的子接口;

  • List集合类中元素有序、可重复,集合中的每个元素都有其对应的顺序索引用以记载其在容器中的位置,可以根据序号存取容器中的元素;

  • List接口的常用实现类:ArrayListLinkedListVector

常用方法

  • List接口存储的元素有序、可重复,故拥有除下Collection接口方法之外的其他方法,还有如下:
方法返回值 方法名 方法的描述
void add(int index, E element) 将指定的元素插入该集合的指定位置
boolean addAll(int index, Collection c) 指定集合中的所有元素插入到该集合的指定位置
E get(int index) 返回此列表中指定index位置的元素。
int indexOf(Object o) 指定元素首次出现的索引,否则返回-1
int lastIndexOf(Object o) 指定元素的最后一次出现的索引,否则返回-1
E remove(int index) 删除该列表中指定索引的元素。
E set(int index, E element) 用指定的元素,替换指定索引的元素。
List<E> subList(int fromIndex, int toIndex) 返回此列表中左闭右开的子集合
  • 代码试验如下:

     @Test
     public void testList(){
          
          
         ArrayList list = new ArrayList();
         list.add("AA");
         list.add("BB");
         list.add(123);
         list.add(456);
         list.add(new Student("tom",18));
         //1、void add(int index, E element) 将指定的元素插入该集合的指定位置
         list.add(2,"CC");
         //2、addAll(int index, Collection c) 指定集合中的所有元素插入到该集合的指定位置
         list.addAll(5,Arrays.asList(78,12,456));
         System.out.println(list);
         //3、E get(int index) 返回此列表中指定index位置的元素。
         System.out.println(list.get(1));
         //4、int indexOf(Object o) 指定元素首次出现的索引,否则返回-1
         System.out.println(list.indexOf("CC"));
         //5、int lastIndexOf(Object o) 指定元素的最后一次出现的索引,否则返回-1
         System.out.println(list.lastIndexOf(456));
         //6、E remove(int index) 删除该列表中指定索引的元素。
         list.remove(6);
         //7、E set(int index, E element) 用指定的元素,替换指定索引的元素。
         list.set(0,"AAA");
         System.out.println(list);
         //8、List<E> subList(int fromIndex, int toIndex)  左闭右开的子集合
         List sublist = list.subList(3,7);
         System.out.println(sublist);
     }
    

实现类:ArrayList

  • ArrayListList 接口的典型实现类、主要实现类,它本质上是对象引用的一个可变的数组。

  • 它的底层实现是数组,不同步,非线程安全,效率高,支持随机访问,查询快,增删慢。

  • 源码简单分析

    //底层是数组 底层Object[] elementData初始化为{}
    public ArrayList() {
          
          
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    //首次添加add()时,数组长度创建为10,并将索引为0的位置添加上元素
    //多次添加后,(到11),扩容,为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
    public boolean add(E e) {
          
          
         ensureCapacityInternal(size + 1);  
         elementData[size++] = e;
         return true;
    }
    

实现类:LinkedList

  • LinkedList底层实现是双向链表,不同步,非线程安全,效率高,查询慢,增删快。

  • 它内部定义了Node类型的firstlast,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。

实现类:Vector

  • Vector底层实现是数组,同步,线程安全,效率低,查询快,增删慢。扩容机制是扩容为原来的两倍。

List接口总结

  • ArrayListVector底层是基于数组实现,查询快;LinkedList底层基于双向链表,增删快。

Set接口

概述

  • Set接口是Collection接口的子接口;

  • Set集合类中元素无序、不可重复;

    • 无序性:存储数据时,按照数据的哈希值来决定该数据在底层数组中的位置,无序性不代表随机性。
    • 不可重复性:添加的数据不重复。详情见下面添加数据的过程。
  • Set接口的常用实现类:HashSetHashSet的子类LinkedHashSetTreeSet

实现类:HashSet

  • HashSetSet 接口的典型实现,使用Hash 算法来存储集合中的元素,存取、查找、删除性能高;

  • 底层实现是:HashMap,无序、不重复、不同步、线程不安全、允许null值;

  • HashSet 添加数据的过程,即如何判断数据是否相同?

    • HashSet中添加元素A时,调用A的hashCode()方法计算出哈希值,该哈希值使用某种算法计算出在底层数组中的存放位置,然后判断该位置是否有元素:

      • 情况1:没有元素、添加成功;

      • 情况2:有其他元素M,比较A与M两者的hash值,

        • 情况1:不相同:添加成功,
        • 情况2:hash值相同,调用A元素的equals()方法,
          • 情况1:返回true,说明元素相同,添加失败,
          • 情况2:返回false,说明元素不相同,添加成功。

实现类:LinkedHashSet

  • LinkedHashSetHashSet的子类,它根据元素的hashCode值来决定元素的存储位置,与此同时,使用双向链表来维护元素的次序,插入性能低于hashSet,
  • 底层实现是:LinkedHashMap,有序,记录元素插入的顺序、不允许重复、不同步,线程不安全、允许null值。

实现类:TreeSet

  • TreeSet确保集合元素处于排序状态,底层实现是:红黑树,不允许重复。

  • 有序

    • 自然排序(默认排序):对象相等标志:compareTo()返回0。

      //学生类实现Comparable  重写compareTo方法。
      public class Student implements Comparable{
              
              
      @Override
          public int compareTo(Object o) {
              
              
              if(o instanceof Student){
              
              
                  Student student = (Student)o;
                  int compare = -this.name.compareTo(student.name);
                  if(compare != 0){
              
              
                      return compare;
                  }else{
              
              
                      return Integer.compare(this.age,student.age);
                  }
              }else{
              
              
                  throw new RuntimeException("输入的类型不匹配");
              }
          }
      }
      
    • 定制排序:对象相等标志,compare()返回0。

      Comparator comparator = new Comparator() {
              
              
           @Override
           public int compare(Object o1, Object o2) {
              
              
               if(o1 instanceof Student && o2 instanceof Student){
              
              
                   Student s1 = (Student)o1;
                   Student s2 = (Student)o2;
                   return Integer.compare(s1.getAge(),s2.getAge());
               }else{
              
              
                   throw new RuntimeException("输入的数据类型不匹配");
               }
           }
       };
      

Set接口总结

  • HashSet是最常用的Set,存储无序不可重复的数据;LinkedHashSet使用双向链表维护元素的次序;TreeSet有两种排序方式:自然排序和定制排序来保证元素有序。

猜你喜欢

转载自blog.csdn.net/W_ryxj000/article/details/108177201