ArrayList
基于数组,数组的增删操作使用方法System.arraycopy()
,扩容机制为每次扩容1.5倍。
1 | /** |
LinkedList
LinkedList
是一个实现了List接口和Deque接口的双端链表,底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性。
LinkedList
基于内部的双端链表节点Node来实现。
HashMap
基于数组和链表,使用拉链法解决哈希冲突,链表长度大于阈值(默认为 8)时,链表转化为红黑树。
添加元素机制:HashMap 通过 key 的 hashCode 经过hash()
方法处理过后得到 hash 值,然后通过 (n - 1) & hash
判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
hash()
方法:哈希值的高位与低位进行异或处理,同时具有高低位的特征,降低哈希冲突的概率。
1 | // jdk 1.8+ |
扩容机制
扩容时机:在put()
时判断是否进行扩容。put()
方法开始时,如果数组为空则进行初始化扩容;put()
方法末尾,判断元素个数是否超过阈值,超过就调用resize()
方法进行扩容。
resize()
方法会遍历hash表中所有的元素,是非常耗时的,在编写程序中,要尽量避免resize()
。
扩容过程:如果数组为空,则生成一个默认大小为16的Node数组;如果数组不为空,则生成一个大小为旧数组大小两倍的新数组,再将就数组元素逐个添加到新数组中,添加的方法为遍历数组,节点数据存在则判断
如果没有后继,则直接添加到hash & (newCap - 1)索引处;
如果有后继,则可能会把链表分成两部分,低索引区和高索引区链表,低索引区就是原来的索引位置,高索引区就是当前索引+oldCap的位置。划分的依据是
hash & oldCap == 0
,为true则放在低索引区,为false则放在高索引区。