ArrayList
数据结构:数组
特点
-
查询快:数组是一块连续的空间,通过首地址,可以找到数组;通过索引可以找到数组中的某一个元素
-
增删慢:数组的长度是固定的,增删一个元素,必须创建一个新的数组,把原数组的数据复制过来
-
private static final int DEFAULT_CAPACITY = 10;
初始容量:10 -
ArrayList<> list = new ArrayList<>(8);
--> 可设置底层数组长度-> initialCapacity = 8if (initialCapacity > 0) { // 初始容量大于0
this.elementData = new Object[initialCapacity]; // 初始化元素数组
}
扩容机制
第一次添加元素时,先调用ensureCapacityInternal(int minCapacity)
方法判断是否为空数组,从而确认最小集合容量minCapacity
-
如果是空数组,给集合最小容量
minCapacity
设置默认容量DEFAULT_CAPACITY
(容量为:10) -
如果不是空数组,调用方法
ensureExplicitCapacity(int minCapacity)
判断是否需要扩容判断条件:if (minCapacity - elementData.length > 0) (minCapacity:集合中 实际元素个数+1)
- 需要扩容:调用
grow(int minCapacity)
,扩容为原来数组elementData
的1.5倍,然后将数组深拷贝赋给(覆盖)原来数组elementData
。 - 不需要扩容,则结束方法。
- 需要扩容:调用
HashMap
数据结构的底层为:位桶(数组)、链表、红黑树
####HashMap的实现特性
- hashMap初始数组长度为16,hashMap数组Entry[]的长度为2的幂次方,数组元素个数大于数组的0.75(加载因子)倍时,会进行2倍的扩容
- put:在存放元素时,先通过hashCode()返回值获取Key的hashCode,然后对hashCode进行计算求出hash值,最后在通过hash值进行取模(位运算:hash&(length-1)[效率更高] --> 等同于 hash%length)算出存放在位桶(数组)哪个位置。(length是数组长度,且为2的幂次方)
- 拿到 key 的 hashCode 值
- 将 hashCode 的高位参与运算,重新计算 hash 值**
- 将计算出来的 **hash 值与 (table.length - 1) **进行 & 运算(table.length是数组的长度)
- 如果有相同 最后计算后的hash值,则以链表形式存放在位桶(数组)相同位置
- 当链表长度>= 8时,将链表转成红黑树存储
为什么初始数组长度为16且为2次幂?
**长度为16的原因:**如果桶初始化桶数组设置太大,就会浪费内存空间,16是一个折中的大小,既不会像1,2,3那样放几个元素就扩容,也不会像几千几万那样可以只会利用一点点空间从而造成大量的浪费。
为2次幂的原因:能利用 & 操作代替 % 操作,提升性能
加载因子为什么为0.75?
加载因子设置为0.75而不是1,是因为设置过大,桶中键值对碰撞的几率就会越大,同一个桶位置可能会存放好几个value值,这样就会增加搜索的时间,性能下降,设置过小也不合适,如果是0.1,那么10个桶,threshold为1,你放两个键值对就要扩容,太浪费空间了。