key-value:HashMap是key-value形式存储的数据结构,key、value都可以为null,但是key只可以有一个null,因为Map的key是不允许重复的。
默认容量:如果在new HashMap的时候没有设置默认值,那么默认容量就是16,如果设置了默认值,那么默认容量就是传入值的向上最接近2的幂等的值。
例如:HashMap map = new HashMap(3); 默认容量就是4;
hash冲突:hashMap是通过链地址法解决hash冲突,多次hash降低hash冲突。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//扰动函数:让高低位都去参与运算
//目的减少hash冲突
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
线程安全:hashMap在并发的时候会有数据丢失,因为多线程去运行,put的时候值会被覆盖,扩容的时候也是不安全的,并发的时候链表会形成死循环。所以hashMap是线程不安全的。
数据结构:hashMap底层数据结构是数组+链表或者数组+红黑树。链表长度大于等于8并且数组长度大于等于64时,链表会转换为红黑树,链表长度小于6时,红黑树会转换为链表。链表长度限制为小于6,也是为了避免链表和红黑树之间频繁转换。
自动扩容:hashMap的扩容因子是0.75,当HashMap中的元素个数超过数组大小的0.75倍时,就会调用resize()方法进行数组扩容,每次扩容的容量都是之前容量的2倍。
首次put时会触发扩容(算是初始化),然后存入数据,然后判断是否需要扩容。
不是首次put,则不再初始化,直接存入数据,然后判断是否需要扩容。
Collections中提供了一个方法synchronizedMap()
Map map = Collections.synchronizedMap(hashMap);
为了避免HashMap的线程安全问题引出了ConcurrentHashMap。
ConcurrentHashMap是由数组、单向链表、红黑树组成的,默认长度是16。key-value都不支持null。
当数组长度大于64并且链表长度大于等于8时,单向链表会转为红黑树,链表长度小于6,红黑树会退化成单向链表。
hach冲突:ConcurrentHashMap是通过链地址法来解决hash冲突的。
并发安全的主要实现是通过对指定的Node节点加锁,来保证数据更新的安全性。
ConcurrentHashMap在性能优化方面主要体现在两点
CAS全称为Compare and swap 比较替换。
假设有三个操作数,内存值V,旧的预期值A,要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值A修改为B并返回true,否则什么都不做。当然cas要与volatile变量配合使用,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,用于是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。
1.7采用数组+单链表,扩容采用头插法,并发情况下链表闭环,采用分段锁,采用的锁是Reentrantlock。
1.8采用数组+红黑树,扩容采用尾插法,采用Node节点,采用的锁是Synchronized+node。
fail-fast:是多线程并发操作集合时的一种失败处理机制,就是最快的实际能把错误抛出而不是让程序执行。
人人都有一个进大厂的梦,希望以上的内容能够对你我这样的人有所帮助。
| 留言与评论(共有 0 条评论) “” |