什么是集合

通俗来讲集合就是用来存放数据的容器。Java的集合中只能存储 对象的引用 (即引用类型),在集合中每一个元素都是一个引用变量,实际内容是存放在堆内或方法区里的;不能存储Java中8种基本数据类型,因为基本数据类型是在栈内存上分配空间的,而栈上的数据随时会被收回,但是因为8种基本数据类型都有对应的包装类(对象),所以当我们将基本数据类型的数据存入集合中,Java会将基本数据类型自动装箱为对应的包装类(如int变为Integer),然后再将引用类型存入到集合中。

集合和数组的区别

数组存放的类型只能是基本数据类型或引用数据类型,集合中存放的数据类型只能是引用数据类型。

数组是静态的,一个数组实例具有固定的大小,一旦创建了不能改变容量。而集合是可以动态扩展容量,可以根据需要动态改变大小。

初始化数组时需要声明存放在数组中的数据类型,而集合可以声明或不声明,当不声明时默认为Object类型,Object是Java中的超类,这就意味着一个集合中可以存放混合类型的数据(既存放String类型的数据又存放Integer类型的数据)

Collection和Collections的区别

java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。

Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

Java集合框架的基础接口

  • Collection ,Collection是集合框架的基础接口之一,在Java中 不提供该接口的任何直接实现。
  • Map ,Map是一个将key映射到value的对象,一个Map不能包含重复的key,每个key最多映射到一个value。
  • List ,List是一个可以包含重复元素的集合
  • Set ,Set是一个不能包含重复元素的集合
  • Queue ,Queue接口是数据结构中队列结构的实现
  • Iterable ,Iterable接口的实现类可以对集合进行遍历(如Iterator)
  • Collection接口相关类图

    Map接口相关类图

    Java集合框架常用集合类

    List接口

    实现List接口的集合是有序的,List接口主要有四个实现类: 分别是ArrayList、Vector、LinkedList和CopyOnWriteArrayList

    如何遍历一个List集合

  • for循环遍历
  • for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    
  • Iterator迭代器遍历
  • //获取迭代器
    Iterator iterator = list.iterator();
    //判断是否还有元素
    while(iterator.hasNext()){
       System.out.println(iterator.next());
    
  • forEach循环遍历。forEach内部也是采用Iterator迭代器实现集合遍历的,在使用时无需显式地声明Iterator迭代器,但是使用forEach遍历List集合时,不允许在遍历的过程中对集合元素进行删除或者修改。
  • for (Object o : list) {
        System.out.println(o);
    
  • 在Java集合中提供了一个RandomAccess(随机存取)接口,如果一个集合实现了该接口,那么该集合就支持随机快速存取,例如ArrayList就实现了该接口,那么它就支持随机快速存取,其底层遍历集合元素时采用的是for循环,遍历或者查询速度都非常快。
  • public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
  • 在集合工具类Collections中,binarySearch()二分查找方法,就运用到这一点,在采用该方法进行元素查找是,会先判断集合是否实现了RandomAccess接口从而判断是使用indexedBinarySearch()[索引] 方法还是iteratorBinarySearch()[迭代器] 方法。
  • public class Collections {
        public static <T>
        int binarySearch(List<? extends Comparable<? super T>> list, T key) {
            if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
                return Collections.indexedBinarySearch(list, key);
                return Collections.iteratorBinarySearch(list, key);
    

    Iterator与ListIterator的区别

    ArrayList使用迭代器遍历集合时,获取迭代器的iterator()方法是List接口中的 iterator()方法,调用该方法返回的是 Iterator对象,而LinkedList获取迭代器的iterator()方法是AbstractSequentialList抽象类中的 iterator()方法,在该方法中又调用了AbstractList抽象类中的listIterator()方法返回一个ListIterator对象

    Iterator和ListIterator区别

  • Iterator可以遍历 Set 和 List 集合,而ListIterator只能用于遍历List。
  • Iterator和ListIterator都可实现删除元素,但是ListIterator可以实现遍历时对元素的修改,用set()方法实现。Iterator仅能遍历,不能修改。
  • Iterator只能单向遍历 [ hasNext()、next()方法)],而ListIterator可以双向遍历(向前/后遍历)[ 从后往前 hasPrevious()、previous()方法 ]
  • ListIterator接口继承于Iterator接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
  • 获取ListIterator方式:List.listIterator()、List.listIterator(int index),index为指定游标的所在的位置。
  • 获取Iterator方式:List.iterator()
  • ListIterator继承Iterator接口后,自己新增的方法

  • void hasPrevious() 判断游标前面是否有元素;
  • Object previous() 返回游标前面的元素,同时游标向前移动一位。
  • int nextIndex() 返回游标后边元素的索引位置,初始为 0,遍历 N 个元素结束时为 N;
  • int previousIndex() 返回游标前面元素的位置,初始时为 -1。
  • void add(E) 在游标前面插入一个元素
  • void set(E) 更新迭代器最后一次操作的元素为 E,也就是更新最后一次调用 next() 或者 previous() 返回的元素。
  • void remove()删除迭代器最后一次操作的元素
  • //ListIterator实现从前向后、从后向前遍历
    List<Integer> list = Arrays.asList(1,2,3,4);
    ListIterator<Integer> listItr = list.listIterator();
    System.out.print("从前向后:");
    while(listItr.hasNext())
        System.out.print(listItr.next()+ "");
    System.out.print("从后向前:");
    while(listItr.hasPrevious())
        System.out.print(listItr.previous());
    

    fast-fail(快速失败)迭代器

    Iterator就是一种fast-fail迭代器,当一个线程通过迭代器遍历List中的元素时,另一个线程修改或删除List中的元素是不允许的(),迭代器会通ConcurrentModificationException异常阻止这种情况发生,这种迭代器就叫做fast-fail迭代器。

    使用forEach循环边遍历边移除元素出现异常

    ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        for (Integer ele : list) {
            list.remove(ele);
        System.out.println(list.size());
    //抛异常
    Exception in thread "main" java.util.ConcurrentModificationException
    	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
    	at java.util.ArrayList$Itr.next(ArrayList.java:861)
    	at com.gjy.demo.collection.list.main(list.java:47)  
    

    编译后如下:

    //编译后:
    ArrayList<Integer> list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        Iterator var2 = list.iterator();
        while(var2.hasNext()) {
            Integer ele = (Integer)var2.next();
            list.remove(ele);
        System.out.println(list.size());
    

    从编译后的代码中可以看出,当我们使用forEach循环一边遍历一边移除元素时,底层其实是用iterator迭代器来遍历的,而移除元素用的却是ArrayList的remove方法,这样迭代器无法感知ArrayList中元素的变化,所以,当用ArrayList的remove()方法移除掉一个元素后,下一次迭代器调用next()方法时会调用checkForComodification()方法用于检测迭代器执行过程中是否有并发修改 (判断modCount 和 expectedModCount是否相等 [集合中有一个modCount属性,在初始化迭代器时,modCount的值会赋给expectedModCount,在迭代的过程中,只要modCount改变了,expectedModCount = modCount等式就不成立] ),如果有则抛出ConcurrentModificationException异常,阻止并发修改ArrayList对象。因此,正确的边遍历边移除元素应该使用Iterator迭代器来实现。

    Set接口

    实现Set接口的集合主要用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。对象的相等本质是根据对象的 hashCode值 ( 在没有重写 hashCode()方法时,调用Object的 hashCode()方法是依据对象的内存地址计算进行计算的 ) 判断的,如果想要让两个不同的对象视为相等的,就必须重写 Object的 hashCode()方法和 equals()方法。

    hashCode()与equals()

  • 如果两个对象相等,则hashcode一定也是相同的,Object类中的hashCode默认是根据对象的内存地址计算算出来的int类型的数值。
  • 两个对象相等,对两个equals方法返回true
  • 两个对象有相同的hashcode值,它们也不一定是相等的
  • 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
  • hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。
  • Set和List的区别

  • List , Set 都是继承自Collection 接口。
  • List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
  • Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及TreeSet。List 支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为它是无序,无法用下标来取得想要的值
  • Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
  • List:和数组类似,List可以动态增长(自动扩容),查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变(如ArrayList、Vector)。
  • Map接口

    Map接口与List和Set接口不同,没有继承Collection,是一个双列集合,它是由一系列键值对组成的集合,提供了key到Value的映射。在Map接口中保证了key与value之间的一一对应关系,即一个key对应一个value,它不能存在相同的key值(唯一)且不要求key是有序的,但是value值是可以相同的。常用的Map实现类有HashMap、HashTable、ConcurrentHashMap.

    以上就是对于Java集合的基本介绍,在接下来的文章中会逐个详细介绍List、Set、Map接口的一些常用实现类,如ArrayList、LinkedList、HashSet、HashMap、ConcurrentHashMap等,欢迎大家关注留意后续内容........

  • 线程池详解
  • synchronized简介
  • ThreadLocal,你掌握了吗?
  • Java异常机制
  • JDK8新特性之Stream流
  • 同学,你对volatile熟悉么?
  • 分类:
    后端
    标签: