List的实现类常用的用
- LinkedList
- ArrayList
- vector
LinkedList:线程不安全
底层结构是双向链表,开销在于需要存储结点信息以及结点指针信息。
双向链表是由三部分来组成的:prev、data、next
- prev:存储上一个节点的地址
- data:存储将要存储的数据
- next:存储下一个节点的地址
双向链表的排序方式是没有顺序的;当我们新增一个元素时,只需要修改前一个元素的next和后一个元素的prev即可,删除元素同理;这样使得LinkedList对于新增和删除的效率大大提高。但是查询数据时,需要一个一个的查找,直到找到为止,使得查询的效率变得很低
get和set慢,add和remove更新快。
ArrayList:线程不安全
底层结构是可变数组,开销在于List列表预留一定空间。
数组存在的位置为在JVM的堆中,用来存储固定大小同类型元素的。当新的元素需要存储时,会存储在最前面,所以每次存储新元素时,所有的元素都会向后移动位置。同理,删除一个元素时,数组中所有的元素都会向前移动位置,所以ArrayList对于增删的效率很低。
数组里面的元素占用的内存相同并且连续排列的。在查询时可以根据数组的下标来进行快速访问,所以ArrayList对于查询效率高。
get和set快,add和remove更新慢。
扩容机制:
- 初始化容量10,初始最大容量Integer.MAX_VALUE - 8
- 先判断,加了新数据之后,原来的容量能不能放下
- 放不下的话,进入扩容方法
- 原容量*1.5(newCapacity = oldCapacity + (oldCapacity >> 1))
- 新容量与minCapacity比较,取较大的值,即扩容一次还不够,就按需取容量
- 如果新的容量超出了Integer.MAX_VALUE - 8,那就用Integer.MAX_VALUE
为什么Integer.MAX_VALUE - 8:
主要区别在于数组对象有一个额外的元数据,用于表示数组的大小。不只是数组的长度信息,还有数组对象的类信息还有同步信息
Vector:线程安全
大致与ArrayList保持一致,有以下几个区别
- 每个方法上都加了synchronized的关键字来保证线程安全,效率降低
- 扩容机制上,维护capacityIncrement增长系数,ArrayList是原容量*1.5,Vector采用newCapacity = oldCapacity + capacityIncrement > 0 ? capacityIncrement : oldCapacity。特就是说存在增长系数就直接加,没有的话就翻倍。
CopyOnWriteArrayList:线程安全
总体基于ArrayList,实现线程安全的方式:CopyOnWriteArrayList通过使用ReentrantLock锁来实现线程安全,写的时候获取副本进行修改,所以不影响读(读写分离)
区别在于:
- 初始容量是0,每次扩容空间刚好够用,不会有多余的空间
- 不能保证数据的实时一致性
Collections.SynchronizedList:线程安全
与Vector区别
- 扩容机制不同,SynchronizedList与ArrayList一致
- SynchronizedList锁代码块,并且可以选择锁的对象,Vector锁方法
- SynchronizedList进行遍历的时候要手动加锁,Vector不用
- SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。:LinkedList直接转成 SynchronizedList但不能转变成Vector,因为底层结构限制