常用集合
ArrayList :变长数组,线程不安全,初始大小10,每次扩容1.5倍;
LinkedList:双链表,读和更新差于ArrayList,增和删好于ArrayList;
Vector:古老的类,线程安全的ArrayList,每次扩容2倍;
HashSet:互异、无序的集合,基于HashMap实现,写入Key中实现去重;
LinkedHashSet:互异、有序的集合,继承自HashSet;
TreeSet:支持自然排序和定制排序,基于TreeMap实现;
HashMap:无序的键值对;
LindekHashMap:内部多了一个链表,保存Key的插入顺序,迭代的时候,比HashMap快;
TreeMap:有序的键值对,可以按照自然排序和定制排序,底层是红黑树;
HashMap
哈希冲突解决方式:
- 开放定址法(向下寻找一个未被使用的空间)
- 再散列函数法
- 链地址法(HashMap使用的方法)
HashMap基础知识:
- HashMap默认长度是16,默认加载因子是0.75;
- HashMap由数组+链表组成的(1.7);
- HashMap由数组+链表/红黑树组成的(1.8);
- HashMap在调用put方法时,才会初始化table(桶数组Entry —— 1.8中变成了Node);
- HashMap的长度一定是2的次幂,为了支持位运算(key根据长度取模),使速度更快;
- 树化阀值:8,多于8,链表转化为红黑树;
- 链化阀值:6,少于6,树转化为链表;
- 允许树化的最小桶数组容量:64;
- 0.75是对空间和时间的折中,使节点在桶中呈泊松分布;
get值
- 取key的hashCode值;
- 高16位与低16位异或,参与运算;
- 与桶数组长度做取模运算,得到数组下标值;
put新值
扩容
扩容条件(需要同时满足):
- 桶数组的被占用的数量大于等于阀值0.75;
- put新值的key在桶数组发生冲突;
扩容过程:
- 新建大数组,2倍,根据新数组的长度重新计算所有元素的hash值;
- 将原值一一拷贝到新数组上;
1.8优化:
节点rehash后,要么不动,要么移动到2倍的位置,所以,1.8将节点的hash值与老数组长度进行与运算,为0的不动,为1的移动到2倍的位置;
扩展点
- HashMap在1.7中链表采用头插法,1.8中链表采用尾插法,是为了在多线程下避免死循环;
- 多线程扩容时,会逆序,导致循环链表的产生,再去get一个不存在的值时,就会死循环;
- 1.7使用头插法是因为他们觉得后插入的元素被查找的频率会更高;
- 1.8的get方法整体优于1.7 15%以上(Hash均匀的情况下);
- Hash不均匀的情况下,1.8远远优于1.7;
- 扩容极度消耗性能,尽可能避免扩容;