List
ArrayList
以数组实现。节约空间,但数组有容量限制。超出限制时会增加50%
容量,用System.arraycopy()
复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时,创建大小为10的数组。
基本优势
按数组下标访问元素get(i)
set(i, e)
的性能很高。
直接在数组末尾加入元素add(e)
的性能也高。
基本劣势
如果按下表插入、删除元素add(i, e)
remove(i)
remove(e)
,则要用System.arraycopy()来移动部分受影响的元素,性能就变差了。
LinkedList
以双向链表实现。链表无容量限制,但本身使用了更多空间,也需要额外的链表指针操作。
基本优势
不需扩容,调整容量
在链表两头的操作add()
addFirst()
removeLast()
能省掉指针的移动
基本劣势
按下标访问元素get(i)
set(i, e)
要遍历链表,将指针移动到位。如果i
>数组大小的一半,会从末尾移起。
CopyOnWriteArrayList
并发优化的ArrayList
。用CopyOnWrite
策略,在修改时,先复制一个快照来修改,改完再让内部指针指向新数组。
因为对快照的修改对读操作不可见,所以只有写锁没有读锁,加上复制的昂贵成本,典型的适合读多写少的场景。如果更新频率较高,或者数组较大时,还是Collections.synchronizedList(list)
,对所有操作用同一把锁来保证线程安全更好。
Map
HashMap
用key
的哈希值%桶数组的大小可得到数组下标。
插入元素时,如果两条key
落在同一个桶,Entry
用一个next
属性实现多个Entry
以单向链表存放,后入桶的Entry
将next
指向桶当前的Entry
当Entry
数量达到桶数量的75%
时,会成倍扩容桶数组,并重新分配所有原来的Entry
,所以这两也最好有个预估值。
取模用位运算(hash & (arrayLength-1))
会比较快,所以数组的大小永远是2的N
次方。默认第一次放入元素时的初始值是16。
在JDK8里,新增默认为8的閥值,当一个桶里的Entry
超过閥值,就不以单向链表而以红黑树来存放以加快Key
的查找速度。
LinkedHashMap
扩展HashMap
,增加了双向链表的实现,号称是最占内存的数据结构。
TreeMap
以红黑树实现。
ConcurrentHashMap
并发优化的HashMap
,默认16把写锁(可以设置更多),有效地分散了阻塞的概率,而且没有读锁。
数据结构为Segment[]
,Segment
里面才是哈希桶数组,每个Segment
一把锁。Key
先算出它在哪个Segment
里,再算出它在哪个哈希桶里。
Set
Set
几乎都是内部用一个Map
来实现的。
将Map
里的KeySet
当做一个Set
,而Value
是假值,全部使用同一个Object
。Set
的特征也继承了那些内部Map
实现的特征。
HashSet
:内部就是HashMap
LinkedHashSet
:内部就是LinkedHashMap
TreeSet
:内部就是TreeMap
的SortedSet
ConcurrentSkipListSet
:内部就是ConcurrentSkipListMap
的并发优化SortedSet
Queue
Queue
是在两端出入的List
,所以也可以用数组或链表来实现。
LinkedList
即是List
,也是Queue
。它是唯一一个允许放入null
值的Queue
。
ArrayDeque
以循环数组实现的双向Queue
。大小是2的倍数,默认是16。
It represens a queue where you can insert and remove elements from both ends of the queue.
Adding and Accessing Elements
The order in which the elements added to the ArrayDeque are stored internally. ArrayDeque stores the elements in the order in which they are inserted.
You can peek at the element at the head of the queue without taking the element out of the queue.
PriorityQueue
用二叉堆实现的优先级队列,不再是FIFO
,而是按元素实现的Comparable
接口或传入Comparator
的比较结果来出队,数值越小,优先级越高,越先出队。