阿里深资架构师花了一个晚上整理出Java集合框架

什么是集合

通俗来讲集合就是用来存放数据的容器。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接口相关类图

image.png

Map接口相关类图

image.png

Java集合框架常用集合类

image.png

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);
        else
            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的值 ] ),如果有则抛出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资料的可以扫一扫  备注【java】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值