
HashMap
Collections.synchronizedMap方法使HashMap具有线程安全的能力LinkedHashMap
TreeMap
java.lang.ClassCastExceptionHashtable
Note: 对于上述四种Map类型的类,要求映射中的key是不可变对象。不可变对象是该对象在创建后它的哈希值不会被改变。如果对象的哈希值发生变化,Map对象很可能就定位不到映射的位置了。
==判断两个对象地址是否相等,即两个对象是否是同一个对象null的引用值x, x.equals(x) 必须返回 truenull的引用值x和y,当且仅当x.equals(y) 返回true时, y.equals(x)必须返回truenull的引用值x,y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回truenull的引用值x和y, 只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true,或者一致的返回falsenull的引用值x,x.equals(null)必须返回false39 @Override
40 public boolean equals(Object obj){
41 if(obj == null){
42 return false;
43 }
44
45 //如果是同一个对象返回true,反之返回false
46 if(this == obj){
47 return true;
48 }
49
50 //判断是否类型相同
51 if(this.getClass() != obj.getClass()){
52 return false;
53 }
54
55 Person person = (Person)obj;
56 return name.equals(person.name) && age==person.age;
57 }
· ^ //异或运算符
·<< or >> or >>> //左移运算符或右移运算符
·13、17、31、1231、1237 //重写 hashCode 方法时常用的素数
·hashCode() //每一个类都自带的方法
·Objects.hashCode(Object o) //return o != null ? o.hashCode() : 0;
·Objects.hash(Object... values) //return Arrays.hashCode(values);
·Arrays.hashCode(Object[] a) 及其重载方法
·包装器的静态方法 hashCode

static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //用来定位数组索引位置
final K key;
V value;
Node<K,V> next; //链表的下一个node
Node(int hash, K key, V value, Node<K,V> next) { ... }
public final K getKey(){ ... }
public final V getValue() { ... }
public final String toString() { ... }
public final int hashCode() { ... }
public final V setValue(V newValue) { ... }
public final boolean equals(Object o) { ... }
}
map.put("美团","小美");
如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组的大小,并在此基础上设计好的hash算法减少Hash碰撞。那么通过什么方式来控制map使得Hash碰撞的概率又小,哈希桶数组(Node[] table)占用空间又少呢?答案就是好的Hash算法和扩容机制。
int threshold; // 所能容纳的key-value对极限
final float loadFactor; // 负载因子
int modCount;
int size;
threshold = length * loadFactor。方法一:
static final int hash(Object key) { //jdk1.8 & jdk1.7
int h;
// h = key.hashCode() 为第一步 取hashCode值
// h ^ (h >>> 16) 为第二步 高位参与运算
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
方法二:
static int indexFor(int h, int length) { //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的
return h & (length-1); //第三步 取模运算
}
取key的hashCode值
高位运算
JDK1.8中,优化了高位运算的算法,主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。
取模运算
在length总是2的n次方的前提下,h & (length -1)(按位与)运算等价于h % length(取模)运算,但&比%有更高的效率。


当HashMap中实际存储的键值对个数size超过threshold时,需要重新resize(扩容)。
1 void resize(int newCapacity) { //传入新的容量
2 Entry[] oldTable = table; //引用扩容前的Entry数组
3 int oldCapacity = oldTable.length;
4 if (oldCapacity == MAXIMUM_CAPACITY) { //扩容前的数组大小如果已经达到最大(2^30)了
5 threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了
6 return;
7 }
8
9 Entry[] newTable = new Entry[newCapacity]; //初始化一个新的Entry数组
10 transfer(newTable); //!!将数据转移到新的Entry数组里
11 table = newTable; //HashMap的table属性引用新的Entry数组
12 threshold = (int)(newCapacity * loadFactor);//修改阈值
13 }
1 void transfer(Entry[] newTable) {
2 Entry[] src = table; //src引用了旧的Entry数组
3 int newCapacity = newTable.length;
4 for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组
5 Entry<K,V> e = src[j]; //取得旧Entry数组的每个元素
6 if (e != null) {
7 src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象)
8 do {
9 Entry<K,V> next = e.next;
10 int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置
11 e.next = newTable[i]; //标记[1]
12 newTable[i] = e; //将元素放在数组上
13 e = next; //访问下一个Entry链上的元素
14 } while (e != null);
15 }
16 }
17 }
在使用迭代器的过程中如果HashMap被修改,那么ConcurrentModificationException将被抛出,也即Fast-fail策略。
HashMap线程不安全,多线程环境会造成死循环(JDK1.8修复),多线程环境建议使用ConcurrentHashMap。
因为在 put 元素的时候,如果触发扩容操作,也就是 rehash ,就会将原数组的内容重新 hash 到新的扩容数组中,但是在扩容这个过程中,其他线程也在进行 put 操作,如果这两个元素 hash 值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
key、value都不为空,否则抛出NullPointerException
支持通过Iterator遍历同时修改HashMap
延迟初始化,在put时才初始化哈希桶(initTable)
Segment分段锁(继承ReentrantLock可重入锁):将整个哈希桶分解成多个Segment对象,每个Segment对象可以看作是一个细粒度的哈希桶;理论上可支持concurrencyLevel(等于Segment的个数)个线程安全的并发读写。

JDK1.8实现
Segment分段锁 -> CAS机制(效率高,但CPU资源消耗多) + synchroized(自旋次数超过阈值时切换位互斥锁)
CAS(Compare and Swap)机制
CAS 通过将内存中的值与期望值进行比较,只有在两者相等时才会对内存中的值进行修改,CAS 是在保证性能的同时提供并发场景下的线程安全性。
SynchronizedMap静态内部类,Object mutex互斥锁,put、get、size 等各种方法加上synchronized关键字put、get、size 等各种方法加上synchronized关键字
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
/**
* The head of the doubly linked list.
* 双向链表的头节点
*/
private transient Entry<K,V> header;
/**
* The iteration ordering method for this linked hash map: true
* for access-order, false for insertion-order.
* true表示最近最少使用次序,false表示插入顺序
*/
private final boolean accessOrder;
}
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
...
}
原文:https://www.cnblogs.com/jay-code-life/p/15217527.html