为什么要使用集合?
当我们需要存储一组类型相同的数据时,数组是最常用且最基本的容器之一。但是,使用数组存储对象存在一些不足之处,因为在实际开发中,存储的数据类型多种多样且数量不确定。这时,Java 集合就派上用场了。与数组相比,Java 集合提供了更灵活、更有效的方法来存储多个数据对象。Java 集合框架中的各种集合类和接口可以存储不同类型和数量的对象,同时还具有多样化的操作方式。相较于数组,Java 集合的优势在于它们的大小可变、支持泛型、具有内建算法等。总的来说,Java 集合提高了数据的存储和处理灵活性,可以更好地适应现代软件开发中多样化的数据需求,并支持高质量的代码编写。
如上集合框架图所示,红色代表接口,蓝色代表抽象类,绿色代表并发包中的类,灰色代表早期线程安全的类(基本已经弃用)。
Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。
List
List是一个接口,常见的实现类有ArrayList和LinkedList,ArrayList的底层数据结构是数组,LinkedList底层数据结构是链表,在开发中用得最多的是ArrayList.
-
ArrayList
数据结构:数组
动态扩容:当我们new ArrayList()的时候,默认会有一个空的Object数组,大小为0,第一次add添加数据时,会给这个数组初始化一个大小,这个大小默认值为10,在每一次add的时候都会判断数组的空间是否够用,如果空间是够的,那直接追加上去就好了。如果不够,就需扩容,每一次扩原来的1.5倍,假设初始化的值是10,但第11个元素要进来时数组就会扩到15,扩容完会把旧数组中的元素拷贝到新数组
是否可以存储null值:ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。
线程安全:线程不安全 -
LinkedList
数据结构:双向链表
线程安全:线程不安全 -
Vector
数据结构:数组
线程安全:线程安全,它的所有的方法都用synchronized进行同步,因此效率较低,基本上已弃用。 -
CopyOnWriteArrayList
数据结构:数组
线程安全:线程安全
原理:CopyOnWriteArrayList是一个线程安全的List,底层是通过复制数组的方式来实现的。读不加锁,写加锁。在执行add()方法时,会加lock锁,复制出一个新的数组,往新的数组里边add元素,最后把旧数组对象指向新的数组。缺点:CopyOnWriteArrayList是很耗费内存的,每次set()/add()都会复制一个数组出来,另外它只能保证数据的最终一致性,不能保证数据的实时一致性。 -
ArrayList 和LinkedList 的区别
是否保证线程安全:ArrayList 和LinkedList都不是线程安全的
底层数据结构:ArrayList 底层使用的是Object 数组,LinkedList 底层使用的是 双向链表数据结构
是否支持快速随机访问:ArrayList支持随机访问,查询效率比较高,LinkedList 不支持随机访问
增删效率:ArrayList 执行add方法时默认在将元素追加到列表的末尾,复杂度就是 O(1),如果要在指定位置插入和删除元素的话会造成数组移动,复杂度就是 O(n),LinkedList插入或者删除元素不受元素位置的影响,时间复杂度都是O(1);
Map
Map是一个接口,常见的实现类有HashMap、LinkedHashMap、HashTable和ConcurrentHashMap
-
HashMap
数据结构:数组+链表/红黑树
动态扩容:HashMap默认的初始化容量为16,负载因子的大小为0.75, 16(容量大小) * 0.75 (负载因子) = 12 (扩容阈值),这里的12表示数组最多只能放12个元素,一旦超过12个元素,哈希表则需要扩容,默认是扩大原数组的2倍。
put方法的实现:在put的时候,首先对key做hash运算,计算出该key所在数组的index,数组的index上没有值,可以直接放到数组index的位置上,如果数组的index上有值说明该元素哈希冲突了,需要通过euqal判断这两元素是否相同,如相同则覆盖,不相同会放到index链表上,当数组的大小大于64且链表的大小大于8的时候才会将链表改为红黑树,当红黑树大小为6时,会退化为链表。
线程安全:线程不安全 -
LinkedHashMap
数据结构:数组+链表/红黑树+双向链表
双向链表的作用:有了这个双向链表,可以使得插入有序
线程安全:线程不安全 -
ConcurrentHashMap
数据结构:数组+链表/红黑树
线程安全:线程安全,ConcurrentHashMap通过在部分加锁和利用CAS算法来实现同步,在get的时候没有加锁,Node都用了volatile给修饰。 -
HashTable
数据结构:数组+链表/红黑树
线程安全:线程安全,所有方法都是使用synchronized进行同步,因此效率较低,基本上已弃用。