HashMap
Collections.synchronizedMap
方法使HashMap具有线程安全的能力LinkedHashMap
TreeMap
java.lang.ClassCastException
Hashtable
Note: 对于上述四种Map类型的类,要求映射中的key是不可变对象。不可变对象是该对象在创建后它的哈希值不会被改变。如果对象的哈希值发生变化,Map对象很可能就定位不到映射的位置了。
==
判断两个对象地址是否相等,即两个对象是否是同一个对象null
的引用值x, x.equals(x)
必须返回 true
null
的引用值x
和y
,当且仅当x.equals(y)
返回true
时, y.equals(x)
必须返回true
null
的引用值x
,y
和z
,如果x.equals(y)
返回true
,并且y.equals(z)
也返回true
,那么x.equals(z)
也必须返回true
null
的引用值x和y, 只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)
就会一致的返回true
,或者一致的返回false
null
的引用值x
,x.equals(null)
必须返回false
39 @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