1.HashMap 和 Hashtable 有什么区别?HashMap 和 HashSet 呢?
HashMap 和 Hashtable 区别:
- hashmap线程不安全、hashtable线程安全
- hashmap继承自abstractmap、hashtable继承dictionary
- hashmap允许存储null键值(存一个null键和多个value是null)、hashtable不允许
- hashtable直接使用hashcode,hashmap用扰动函数处理hashcode(hashcode右移16位在与原hashcode异或操作,为了减少hashcode冲突)
- hashmap初始容量16,hashtable11
- hashmap扩容old*2;hashtable扩容old*2+1
hashset:
- 底层实现是hashmap,因为hashset内部元素不能相同,所以存储方式为hashmap的key值。
- 需要重写hashcode函数和equals函数,通过hashcode直接定位到地址,在通过equals比较对象。
- 元素无序
2.final 关键字用于什么场景?
- 字段不可修改,引用不可修改地址
- 方法不能重写
- 类不能继承
包含final域的对象的引用和读这个final域,不能重排序;构造函数对final域的写入和这个对象的引用被赋值,不能重排序。
使用场景:
- 不可改变域
- 多线程使用场景,使用final关键字或者:synchronized、volatile、锁
3.ConcurrentHashMap 如何实现线程同步?
hashmap的线程安全版,引入segment,每一个segment都是线程安全的,相当于一个hashtable,因此,ConcurrentHashMap也不允许出现null。这样就把整个类锁变成了局部锁,用哪一个segment就给哪一个segment加锁。减少竞争,提高效率。
对于jdk1.8的改进:
- 取消的segment,转而采用数组元素作为锁。把锁的粒度从多个node变成一个node,进一步减少锁竞争
- 链表大于8的时候转化为红黑树
实现线程同步:
元素Node,字段修饰为final和volatile,采用乐观锁CAS,和分而治之的思想
-
put操作和初始化操作:
- volatile字段,标识位,表示当前是否有线程在初始化,volatile字段保证了所有线程的可见。
- CAS机制,保证只有一个线程能够初始化
-
size()/判断大小
首先通过CAS机制,如果没有线程竞争,直接递增count,失败就初始化桶,每一个桶并发的记录(同样是CAS机制,最大程度利用并发),如果桶计数频繁失败就扩容桶。
4.Map 遍历的两种方式?
keyset和entryset,前者是获得key的集合,后者是获得key-value的集合,返回的都是set视图,因为set有迭代器iterator可以用,通过iterator.next来遍历。