目录
Collection
List
ArrayList
Vector
LinkedList
Set
HshSet
TreeSet
LinkedHashSet
Queue
Map
HashMap
ConcurrentHashMap
HashTable
TreeMap
LinkedHashMap
1. Colection
1.1 List
1.1.1 ArrayList
底层由数组实现;
插入代价大:O(n);
适合进行查询操作:O(1);
线程不安全;
实例化ArrayLisr时不初始化容量,第一次调用add()方法时进行长度初始化,初始长度:10;
扩容:容量不足时扩容,使用向右移位,则为当前容量的1.5倍;
int newCapacity = oldCapacity + (oldCapacity >> 1);
1.1.2 Vector
底层由数组实现;
支持线程同步(synchronized);
效率低于ArrayList(由于支持线程同步)
初始化时则初始化容量:10;
扩容:每次扩容为原来两倍;
int newCapacity = oldCapacity * 2;
1.1.2 LinkedList
链表结构
插入、删除操作速度快O(n);
1.2 Set
1.2.1 HashSet
不按照存入顺序存储
当 hashcode相等 && equals返回true 时使用链表向下顺延;
1.2.2 TreeSet
1.2.3 LinkedHshSet
2 Map
2.1 HashMap
2.1.1 java 7
非线程安全
底层实现:数组+链表
重要参数:
Capacity:数组容量,保持2n,初始值为16,扩容后为当前容量2倍;(为什么为2n:为了使散列更均匀,极端情况下查询效率将由O(1)下降为O(n))
LoadFactor:负载因子,默认为0.75;
Threadhold:扩容阈值,Threadhold = Capacity * LoadFactor,当存储量超过扩容阈值时,Hashmap将进行扩容。
2.1.2 java 8
非线程安全
底层实现:数组+链表+红黑树
引入红黑树,查询效率由O(n)下降为O(logn)
1、put(key, value)中直接调用了内部的putVal方法,并且先对key进行了hash操作;
2、putVal方法中,先检查HashMap数据结构中的索引数组表是否位空,如果是的话则进行一次resize操作;
3、以HashMap索引数组表的长度减一与key的hash值进行与运算,得出在数组中的索引,如果索引指定的位置值为空,则新建一个k-v的新节点;
4、如果不满足的3的条件,则说明索引指定的数组位置的已经存在内容,这个时候称之碰撞出现;
5、在上面判断流程走完之后,计算HashMap全局的modCount值,以便对外部并发的迭代操作提供修改的Fail-fast判断提供依据,于此同时增加map中的记录数,并判断记录数是否触及容量扩充的阈值,触及则进行一轮resize操作;
6、在步骤4中出现碰撞情况时,从步骤7开始展开新一轮逻辑判断和处理;
7、判断key索引到的节点(暂且称作被碰撞节点)的hash、key是否和当前待插入节点(新节点)的一致,如果是一致的话,则先保存记录下该节点;如果新旧节点的内容不一致时,则再看被碰撞节点是否是树(TreeNode)类型,如果是树类型的话,则按照树的操作去追加新节点内容;如果被碰撞节点不是树类型,则说明当前发生的碰撞在链表中(此时链表尚未转为红黑树),此时进入一轮循环处理逻辑中;
8、循环中,先判断被碰撞节点的后继节点是否为空,为空则将新节点作为后继节点,作为后继节点之后并判断当前链表长度是否超过最大允许链表长度8,如果大于的话,需要进行一轮是否转树的操作;如果在一开始后继节点不为空,则先判断后继节点是否与新节点相同,相同的话就记录并跳出循环;如果两个条件判断都满足则继续循环,直至进入某一个条件判断然后跳出循环;
9、步骤8中转树的操作treeifyBin,如果map的索引表为空或者当前索引表长度还小于64(最大转红黑树的索引数组表长度),那么进行resize操作就行了;否则,如果被碰撞节点不为空,那么就顺着被碰撞节点这条树往后新增该新节点;
10、最后,回到那个被记住的被碰撞节点,如果它不为空,默认情况下,新节点的值将会替换被碰撞节点的值,同时返回被碰撞节点的值(V)。
1、在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失;
2、在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
树化与链化
链表内元素 ≥ 8
数组长度<64:扩容数组;
数组长度 ≥ 64:链表树化;
链表内元素<6
红黑树链化;
2.2 ConcurrentHashMap
2.2.1 java 7
底层实现:Segment + ReentrantLock + 分段锁
并行度:16,最多支持16个线程并发写(并行数由Segment数量决定)
2.2.2 java 8
底层实现: 数组 + CAS + Synchronized
ConcurrentHashMap之put方法
1、计算hashcode;
2、table为空,则执行初始化,默认长度16;(基于CAS)
3、否则,计算hashcode在数组中对应的下标,并获取下标对应的头结点;
4、头结点为空,添加头结点;(基于CAS)
5、头结点不为空,但头结点值为 MOVED ,表示正在扩容,帮助其扩容线程扩容(基于CAS)
6、否则,头结点不为空,且未处于扩容状态,尝试添加新结点(基于Synchronized)
7、判断当前桶内节点数量是否大于阈值,大于则扩容 (基于CAS)
链表内元素 ≥ 8
数组长度<64:扩容数组;
数组长度 ≥ 64:链表树化;
链表内元素<6
红黑树链化;
参考: