好文。
http://www.jasongj.com/java/concurrenthashmap/

HashMap

https://www.yuque.com/docs/share/ded15ad2-6d64-4bbe-9a31-b83bb0a40675?# 《HashMap源码分析》

HashTable

Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。
Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中。
Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。

  1. package java.util;
  2. import java.io.*;
  3. public class Hashtable<K,V>
  4. extends Dictionary<K,V>
  5. implements Map<K,V>, Cloneable, java.io.Serializable {
  6. // 保存key-value的数组。
  7. // Hashtable同样采用单链表解决冲突,每一个Entry本质上是一个单向链表
  8. private transient Entry[] table;
  9. // Hashtable中键值对的数量
  10. private transient int count;
  11. // 阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子)
  12. private int threshold;
  13. // 加载因子
  14. private float loadFactor;
  15. // Hashtable被改变的次数,用于fail-fast机制的实现
  16. private transient int modCount = 0;
  17. // 序列版本号
  18. private static final long serialVersionUID = 1421746759512286392L;
  19. // 指定“容量大小”和“加载因子”的构造函数
  20. public Hashtable(int initialCapacity, float loadFactor) {
  21. if (initialCapacity < 0)
  22. throw new IllegalArgumentException("Illegal Capacity: "+
  23. initialCapacity);
  24. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  25. throw new IllegalArgumentException("Illegal Load: "+loadFactor);
  26. if (initialCapacity==0)
  27. initialCapacity = 1;
  28. this.loadFactor = loadFactor;
  29. table = new Entry[initialCapacity];
  30. threshold = (int)(initialCapacity * loadFactor);
  31. }
  32. // 指定“容量大小”的构造函数
  33. public Hashtable(int initialCapacity) {
  34. this(initialCapacity, 0.75f);
  35. }
  36. // 默认构造函数。
  37. public Hashtable() {
  38. // 默认构造函数,指定的容量大小是11;加载因子是0.75
  39. this(11, 0.75f);
  40. }
  41. // 包含“子Map”的构造函数
  42. public Hashtable(Map<? extends K, ? extends V> t) {
  43. this(Math.max(2*t.size(), 11), 0.75f);
  44. // 将“子Map”的全部元素都添加到Hashtable中
  45. putAll(t);
  46. }
  47. public synchronized int size() {
  48. return count;
  49. }
  50. public synchronized boolean isEmpty() {
  51. return count == 0;
  52. }
  53. // 返回“所有key”的枚举对象
  54. public synchronized Enumeration<K> keys() {
  55. return this.<K>getEnumeration(KEYS);
  56. }
  57. // 返回“所有value”的枚举对象
  58. public synchronized Enumeration<V> elements() {
  59. return this.<V>getEnumeration(VALUES);
  60. }
  61. // 判断Hashtable是否包含“值(value)”
  62. public synchronized boolean contains(Object value) {
  63. //注意,Hashtable中的value不能是null,
  64. // 若是null的话,抛出异常!
  65. if (value == null) {
  66. throw new NullPointerException();
  67. }
  68. // 从后向前遍历table数组中的元素(Entry)
  69. // 对于每个Entry(单向链表),逐个遍历,判断节点的值是否等于value
  70. Entry tab[] = table;
  71. for (int i = tab.length ; i-- > 0 ;) {
  72. for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
  73. if (e.value.equals(value)) {
  74. return true;
  75. }
  76. }
  77. }
  78. return false;
  79. }
  80. public boolean containsValue(Object value) {
  81. return contains(value);
  82. }
  83. // 判断Hashtable是否包含key
  84. public synchronized boolean containsKey(Object key) {
  85. Entry tab[] = table;
  86. //计算hash值,直接用key的hashCode代替
  87. int hash = key.hashCode();
  88. // 计算在数组中的索引值
  89. int index = (hash & 0x7FFFFFFF) % tab.length;
  90. // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
  91. for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
  92. if ((e.hash == hash) && e.key.equals(key)) {
  93. return true;
  94. }
  95. }
  96. return false;
  97. }
  98. // 返回key对应的value,没有的话返回null
  99. public synchronized V get(Object key) {
  100. Entry tab[] = table;
  101. int hash = key.hashCode();
  102. // 计算索引值,
  103. int index = (hash & 0x7FFFFFFF) % tab.length;
  104. // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
  105. for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
  106. if ((e.hash == hash) && e.key.equals(key)) {
  107. return e.value;
  108. }
  109. }
  110. return null;
  111. }
  112. // 调整Hashtable的长度,将长度变成原来的2倍+1
  113. protected void rehash() {
  114. int oldCapacity = table.length;
  115. Entry[] oldMap = table;
  116. //创建新容量大小的Entry数组
  117. int newCapacity = oldCapacity * 2 + 1;
  118. Entry[] newMap = new Entry[newCapacity];
  119. modCount++;
  120. threshold = (int)(newCapacity * loadFactor);
  121. table = newMap;
  122. //将“旧的Hashtable”中的元素复制到“新的Hashtable”中
  123. for (int i = oldCapacity ; i-- > 0 ;) {
  124. for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
  125. Entry<K,V> e = old;
  126. old = old.next;
  127. //重新计算index
  128. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  129. e.next = newMap[index];
  130. newMap[index] = e;
  131. }
  132. }
  133. }
  134. // 将“key-value”添加到Hashtable中
  135. public synchronized V put(K key, V value) {
  136. // Hashtable中不能插入value为null的元素!!!
  137. if (value == null) {
  138. throw new NullPointerException();
  139. }
  140. // 若“Hashtable中已存在键为key的键值对”,
  141. // 则用“新的value”替换“旧的value”
  142. Entry tab[] = table;
  143. int hash = key.hashCode();
  144. int index = (hash & 0x7FFFFFFF) % tab.length;
  145. for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
  146. if ((e.hash == hash) && e.key.equals(key)) {
  147. V old = e.value;
  148. e.value = value;
  149. return old;
  150. }
  151. }
  152. // 若“Hashtable中不存在键为key的键值对”,
  153. // 将“修改统计数”+1
  154. modCount++;
  155. // 若“Hashtable实际容量” > “阈值”(阈值=总的容量 * 加载因子)
  156. // 则调整Hashtable的大小
  157. if (count >= threshold) {
  158. rehash();
  159. tab = table;
  160. index = (hash & 0x7FFFFFFF) % tab.length;
  161. }
  162. //将新的key-value对插入到tab[index]处(即链表的头结点)
  163. Entry<K,V> e = tab[index];
  164. tab[index] = new Entry<K,V>(hash, key, value, e);
  165. count++;
  166. return null;
  167. }
  168. // 删除Hashtable中键为key的元素
  169. public synchronized V remove(Object key) {
  170. Entry tab[] = table;
  171. int hash = key.hashCode();
  172. int index = (hash & 0x7FFFFFFF) % tab.length;
  173. //从table[index]链表中找出要删除的节点,并删除该节点。
  174. //因为是单链表,因此要保留带删节点的前一个节点,才能有效地删除节点
  175. for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
  176. if ((e.hash == hash) && e.key.equals(key)) {
  177. modCount++;
  178. if (prev != null) {
  179. prev.next = e.next;
  180. } else {
  181. tab[index] = e.next;
  182. }
  183. count--;
  184. V oldValue = e.value;
  185. e.value = null;
  186. return oldValue;
  187. }
  188. }
  189. return null;
  190. }
  191. // 将“Map(t)”的中全部元素逐一添加到Hashtable中
  192. public synchronized void putAll(Map<? extends K, ? extends V> t) {
  193. for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
  194. put(e.getKey(), e.getValue());
  195. }
  196. // 清空Hashtable
  197. // 将Hashtable的table数组的值全部设为null
  198. public synchronized void clear() {
  199. Entry tab[] = table;
  200. modCount++;
  201. for (int index = tab.length; --index >= 0; )
  202. tab[index] = null;
  203. count = 0;
  204. }
  205. // 克隆一个Hashtable,并以Object的形式返回。
  206. public synchronized Object clone() {
  207. try {
  208. Hashtable<K,V> t = (Hashtable<K,V>) super.clone();
  209. t.table = new Entry[table.length];
  210. for (int i = table.length ; i-- > 0 ; ) {
  211. t.table[i] = (table[i] != null)
  212. ? (Entry<K,V>) table[i].clone() : null;
  213. }
  214. t.keySet = null;
  215. t.entrySet = null;
  216. t.values = null;
  217. t.modCount = 0;
  218. return t;
  219. } catch (CloneNotSupportedException e) {
  220. throw new InternalError();
  221. }
  222. }
  223. public synchronized String toString() {
  224. int max = size() - 1;
  225. if (max == -1)
  226. return "{}";
  227. StringBuilder sb = new StringBuilder();
  228. Iterator<Map.Entry<K,V>> it = entrySet().iterator();
  229. sb.append('{');
  230. for (int i = 0; ; i++) {
  231. Map.Entry<K,V> e = it.next();
  232. K key = e.getKey();
  233. V value = e.getValue();
  234. sb.append(key == this ? "(this Map)" : key.toString());
  235. sb.append('=');
  236. sb.append(value == this ? "(this Map)" : value.toString());
  237. if (i == max)
  238. return sb.append('}').toString();
  239. sb.append(", ");
  240. }
  241. }
  242. // 获取Hashtable的枚举类对象
  243. // 若Hashtable的实际大小为0,则返回“空枚举类”对象;
  244. // 否则,返回正常的Enumerator的对象。
  245. private <T> Enumeration<T> getEnumeration(int type) {
  246. if (count == 0) {
  247. return (Enumeration<T>)emptyEnumerator;
  248. } else {
  249. return new Enumerator<T>(type, false);
  250. }
  251. }
  252. // 获取Hashtable的迭代器
  253. // 若Hashtable的实际大小为0,则返回“空迭代器”对象;
  254. // 否则,返回正常的Enumerator的对象。(Enumerator实现了迭代器和枚举两个接口)
  255. private <T> Iterator<T> getIterator(int type) {
  256. if (count == 0) {
  257. return (Iterator<T>) emptyIterator;
  258. } else {
  259. return new Enumerator<T>(type, true);
  260. }
  261. }
  262. // Hashtable的“key的集合”。它是一个Set,没有重复元素
  263. private transient volatile Set<K> keySet = null;
  264. // Hashtable的“key-value的集合”。它是一个Set,没有重复元素
  265. private transient volatile Set<Map.Entry<K,V>> entrySet = null;
  266. // Hashtable的“key-value的集合”。它是一个Collection,可以有重复元素
  267. private transient volatile Collection<V> values = null;
  268. // 返回一个被synchronizedSet封装后的KeySet对象
  269. // synchronizedSet封装的目的是对KeySet的所有方法都添加synchronized,实现多线程同步
  270. public Set<K> keySet() {
  271. if (keySet == null)
  272. keySet = Collections.synchronizedSet(new KeySet(), this);
  273. return keySet;
  274. }
  275. // Hashtable的Key的Set集合。
  276. // KeySet继承于AbstractSet,所以,KeySet中的元素没有重复的。
  277. private class KeySet extends AbstractSet<K> {
  278. public Iterator<K> iterator() {
  279. return getIterator(KEYS);
  280. }
  281. public int size() {
  282. return count;
  283. }
  284. public boolean contains(Object o) {
  285. return containsKey(o);
  286. }
  287. public boolean remove(Object o) {
  288. return Hashtable.this.remove(o) != null;
  289. }
  290. public void clear() {
  291. Hashtable.this.clear();
  292. }
  293. }
  294. // 返回一个被synchronizedSet封装后的EntrySet对象
  295. // synchronizedSet封装的目的是对EntrySet的所有方法都添加synchronized,实现多线程同步
  296. public Set<Map.Entry<K,V>> entrySet() {
  297. if (entrySet==null)
  298. entrySet = Collections.synchronizedSet(new EntrySet(), this);
  299. return entrySet;
  300. }
  301. // Hashtable的Entry的Set集合。
  302. // EntrySet继承于AbstractSet,所以,EntrySet中的元素没有重复的。
  303. private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
  304. public Iterator<Map.Entry<K,V>> iterator() {
  305. return getIterator(ENTRIES);
  306. }
  307. public boolean add(Map.Entry<K,V> o) {
  308. return super.add(o);
  309. }
  310. // 查找EntrySet中是否包含Object(0)
  311. // 首先,在table中找到o对应的Entry链表
  312. // 然后,查找Entry链表中是否存在Object
  313. public boolean contains(Object o) {
  314. if (!(o instanceof Map.Entry))
  315. return false;
  316. Map.Entry entry = (Map.Entry)o;
  317. Object key = entry.getKey();
  318. Entry[] tab = table;
  319. int hash = key.hashCode();
  320. int index = (hash & 0x7FFFFFFF) % tab.length;
  321. for (Entry e = tab[index]; e != null; e = e.next)
  322. if (e.hash==hash && e.equals(entry))
  323. return true;
  324. return false;
  325. }
  326. // 删除元素Object(0)
  327. // 首先,在table中找到o对应的Entry链表
  328. // 然后,删除链表中的元素Object
  329. public boolean remove(Object o) {
  330. if (!(o instanceof Map.Entry))
  331. return false;
  332. Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
  333. K key = entry.getKey();
  334. Entry[] tab = table;
  335. int hash = key.hashCode();
  336. int index = (hash & 0x7FFFFFFF) % tab.length;
  337. for (Entry<K,V> e = tab[index], prev = null; e != null;
  338. prev = e, e = e.next) {
  339. if (e.hash==hash && e.equals(entry)) {
  340. modCount++;
  341. if (prev != null)
  342. prev.next = e.next;
  343. else
  344. tab[index] = e.next;
  345. count--;
  346. e.value = null;
  347. return true;
  348. }
  349. }
  350. return false;
  351. }
  352. public int size() {
  353. return count;
  354. }
  355. public void clear() {
  356. Hashtable.this.clear();
  357. }
  358. }
  359. // 返回一个被synchronizedCollection封装后的ValueCollection对象
  360. // synchronizedCollection封装的目的是对ValueCollection的所有方法都添加synchronized,实现多线程同步
  361. public Collection<V> values() {
  362. if (values==null)
  363. values = Collections.synchronizedCollection(new ValueCollection(),
  364. this);
  365. return values;
  366. }
  367. // Hashtable的value的Collection集合。
  368. // ValueCollection继承于AbstractCollection,所以,ValueCollection中的元素可以重复的。
  369. private class ValueCollection extends AbstractCollection<V> {
  370. public Iterator<V> iterator() {
  371. return getIterator(VALUES);
  372. }
  373. public int size() {
  374. return count;
  375. }
  376. public boolean contains(Object o) {
  377. return containsValue(o);
  378. }
  379. public void clear() {
  380. Hashtable.this.clear();
  381. }
  382. }
  383. // 重新equals()函数
  384. // 若两个Hashtable的所有key-value键值对都相等,则判断它们两个相等
  385. public synchronized boolean equals(Object o) {
  386. if (o == this)
  387. return true;
  388. if (!(o instanceof Map))
  389. return false;
  390. Map<K,V> t = (Map<K,V>) o;
  391. if (t.size() != size())
  392. return false;
  393. try {
  394. // 通过迭代器依次取出当前Hashtable的key-value键值对
  395. // 并判断该键值对,存在于Hashtable中。
  396. // 若不存在,则立即返回false;否则,遍历完“当前Hashtable”并返回true。
  397. Iterator<Map.Entry<K,V>> i = entrySet().iterator();
  398. while (i.hasNext()) {
  399. Map.Entry<K,V> e = i.next();
  400. K key = e.getKey();
  401. V value = e.getValue();
  402. if (value == null) {
  403. if (!(t.get(key)==null && t.containsKey(key)))
  404. return false;
  405. } else {
  406. if (!value.equals(t.get(key)))
  407. return false;
  408. }
  409. }
  410. } catch (ClassCastException unused) {
  411. return false;
  412. } catch (NullPointerException unused) {
  413. return false;
  414. }
  415. return true;
  416. }
  417. // 计算Entry的hashCode
  418. // 若 Hashtable的实际大小为0 或者 加载因子<0,则返回0。
  419. // 否则,返回“Hashtable中的每个Entry的key和value的异或值 的总和”。
  420. public synchronized int hashCode() {
  421. int h = 0;
  422. if (count == 0 || loadFactor < 0)
  423. return h; // Returns zero
  424. loadFactor = -loadFactor; // Mark hashCode computation in progress
  425. Entry[] tab = table;
  426. for (int i = 0; i < tab.length; i++)
  427. for (Entry e = tab[i]; e != null; e = e.next)
  428. h += e.key.hashCode() ^ e.value.hashCode();
  429. loadFactor = -loadFactor; // Mark hashCode computation complete
  430. return h;
  431. }

针对Hashtable,我们同样给出几点比较重要的总结,但要结合与HashMap的比较来总结

  1. 二者的存储结构和解决冲突的方法都是相同的
  2. HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
  3. Hashtable中key和value都不允许为null,而HashMap中key和value都允许为null(key只能有一个为null,而value则可以有多个为null)。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
  4. Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
  5. Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。

HashTable是线程安全的。

LinkedHashMap

image.png
类继承关系图表
LinkedHashMap 直接继承自HashMapHashMap 一切重要的概念 LinkedHashMap 都是拥有的,这就包括了,hash 算法定位 hash 桶位置,哈希表由数组和单链表构成,并且当单链表长度超过 8 的时候转化为红黑树,扩容体系,这一切都跟 HashMap 一样。那么除了这么多关键的相同点以外,LinkedHashMapHashMap 更加强大,这体现在:

  • **LinkedHashMap 内部维护了一个双向链表,解决了 不能随时保持遍历顺序和插入顺序一致的问题**``**HashMap**
  • **LinkedHashMap 元素的访问顺序也提供了相关支持,也就是我们常说的 LRU(最近最少使用)原则。**

    LinkedHashMap 双向链表的构建过程

    Map 源码合集 - 图2
    HashMap 的存储结构为,数组 + 单链表 + 红黑树,从上边的图片我们也可以看出 LinkedHashMap 底层的存储结构并没有发生变化。
    唯一变化的是使用双向链表(图中红黄箭头部分)记录了元素的添加顺序,我们知道 HashMap 中的 Node 节点只有 next 指针,对于双向链表而言只有 next 指针是不够的,所以 LinkedHashMap 对于 Node 节点进行了拓展:
    1. static class Entry<K,V> extends HashMap.Node<K,V> {
    2. Entry<K,V> before, after;
    3. Entry(int hash, K key, V value, Node<K,V> next) {
    4. super(hash, key, value, next);
    5. }
    6. }
    LinkedHashMap 基本存储单元 Entry<K,V> 继承自 HashMap.Node<K,V>,并在此基础上添加了 before 和 after 这两个指针变量。这 before 变量在每次添加元素的时候将会链接上一次添加的元素,而上一次添加的元素的 after 变量将指向该次添加的元素,来形成双向链接。值得注意的是 LinkedHashMap 并没有覆写任何关于 HashMap put 方法。所以调用 LinkedHashMap 的 put 方法实际上调用了父类 HashMap 的方法。
    HashMapnewNode 方法无法完成上述所讲的双向链表节点的间的关系,所以 LinkedHashMap 复写了该方法: ```java // HashMap newNode 中实现 Node newNode(int hash, K key, V value, Node next) { return new Node<>(hash, key, value, next); }

// LinkedHashMap newNode 的实现 Node newNode(int hash, K key, V value, Node e) { LinkedHashMap.Entry p = new LinkedHashMap.Entry(hash, key, value, e); // 将 Entry 接在双向链表的尾部 linkNodeLast(p); return p; }

  1. ```java
  2. /**
  3. * 该引用始终指向双向链表的头部
  4. */
  5. transient LinkedHashMap.Entry<K,V> head;
  6. /**
  7. * 该引用始终指向双向链表的尾部
  8. */
  9. transient LinkedHashMap.Entry<K,V> tail;
  10. // newNode 中新节点,放到双向链表的尾部
  11. private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
  12. // 添加元素之前双向链表尾部节点
  13. LinkedHashMap.Entry<K,V> last = tail;
  14. // tail 指向新添加的节点
  15. tail = p;
  16. //如果之前 tail 指向 null 那么集合为空新添加的节点 head = tail = p
  17. if (last == null)
  18. head = p;
  19. else {
  20. // 否则将新节点的 before 引用指向之前当前链表尾部
  21. p.before = last;
  22. // 当前链表尾部节点的 after 指向新节点
  23. last.after = p;
  24. }
  25. }

LinkedHashMap 通过调用父类的 HashMap 的 remove 方法将 Hash 表的中节点的删除操作完成即:

  1. 获取对应 key 的哈希值 hash(key),定位对应的哈希桶的位置
  2. 遍历对应的哈希桶中的单链表或者红黑树找到对应 key 相同的节点,在最后删除,并返回原来的节点。

对于 afterNodeRemoval(node) HashMap 中是空实现,而该方法,正是 LinkedHashMap 删除对应节点在双向链表中的关系的操作:

  1. // 从双向链表中删除对应的节点 e 为已经删除的节点
  2. void afterNodeRemoval(Node<K,V> e) {
  3. LinkedHashMap.Entry<K,V> p =
  4. (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  5. // 将 p 节点的前后指针引用置为 null 便于内存释放
  6. p.before = p.after = null;
  7. // p.before 为 null,表明 p 是头节点
  8. if (b == null)
  9. head = a;
  10. else//否则将 p 的前驱节点连接到 p 的后驱节点
  11. b.after = a;
  12. // a 为 null,表明 p 是尾节点
  13. if (a == null)
  14. tail = b;
  15. else //否则将 a 的前驱节点连接到 b
  16. a.before = b;
  17. }

LinkedHashMap 维护节点访问顺序

  1. HashMap 的遍历结果是跟添加顺序并无关系
  2. LinkedHashMap 的遍历结果就是添加顺序

这就是双向链表的作用。双向链表能做的不仅仅是这些,在介绍双向链表维护访问顺序前我们看来看一个重要的参数:

  1. final boolean accessOrder;// 是否维护双向链表中的元素访问顺序

该方法随 LinkedHashMap 构造参数初始化,accessOrder 默认值为 false.
可以看出当我们使用 access 为 true 后,我们访问元素的顺序将会在下次遍历的时候体现,最后访问的元素将最后获得。其实这一切在 HashMap 源码中 putVal/get/repalce 最后都有一个 void afterNodeAccess(Node<K,V> e) 方法,该方法在 HashMap 中是空实现,但是在 LinkedHasMap 中该后置方法,将作为维护节点访问顺序的重要方法,我们来看下其实现:

  1. //将被访问节点移动到链表最后
  2. void afterNodeAccess(Node<K,V> e) { // move node to last
  3. LinkedHashMap.Entry<K,V> last;
  4. if (accessOrder && (last = tail) != e) {
  5. LinkedHashMap.Entry<K,V> p =
  6. (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  7. //访问节点的后驱置为 null
  8. p.after = null;
  9. //如访问节点的前驱为 null 则说明 p = head
  10. if (b == null)
  11. head = a;
  12. else
  13. b.after = a;
  14. //如果 p 不为尾节点 那么将 a 的前驱设置为 b
  15. if (a != null)
  16. a.before = b;
  17. else
  18. last = b;
  19. if (last == null)
  20. head = p;
  21. else {
  22. p.before = last;
  23. last.after = p;
  24. }
  25. tail = p;// 将 p 接在双向链表的最后
  26. ++modCount;
  27. }
  28. }

Java 中最简单的 LRU 构建方式

LRU(Least Recently Used) 算法实现的关键就像它名字一样,当达到预定阈值的时候,这个阈值可能是内存不足,或者容量达到最大,找到最近最少使用的存储元素进行移除,保证新添加的元素能够保存到集合中。
HashMap 的 putVal 方法添加完元素后还有个后置操作void afterNodeInsertion(boolean evict){} 就是这个方法。 LinkedHashMap 重写了此方法:

  1. // HashMap 中 putVal 方法实现 evict 传递的 true,表示表处于创建模式。
  2. public V put(K key, V value) {
  3. return putVal(hash(key), key, value, false, true);
  4. }
  5. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  6. boolean evict) { .... }
  7. //evict 由上述说明大部分情况下都传 true 表示表处于创建模式
  8. void afterNodeInsertion(boolean evict) { // possibly remove eldest
  9. LinkedHashMap.Entry<K,V> first;
  10. //由于 evict = true 那么当链表不为空的时候 且 removeEldestEntry(first) 返回 true 的时候进入if 内部
  11. if (evict && (first = head) != null && removeEldestEntry(first)) {
  12. K key = first.key;
  13. removeNode(hash(key), key, null, false, true);//移除双向链表中处于 head 的节点
  14. }
  15. }
  16. //LinkedHashMap 默认返回 false 则不删除节点。 返回 true 双向链表中处于 head 的节点
  17. protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
  18. return false;
  19. }

由上述源码可以看出,如果 removeEldestEntry(Map.Entry<K,V> eldest) 方法返回值为 true 的时候,当我们添加一个新的元素之后,afterNodeInsertion这个后置操作,将会删除双向链表最初的节点,也就是 head 节点。

TreeMap

image.png
TreeMap继承于AbstractMap,实现了Map, Cloneable, NavigableMap, Serializable接口。
(1)TreeMap 继承于AbstractMap,而AbstractMap实现了Map接口,并实现了Map接口中定义的方法,减少了其子类继承的复杂度;
(2)TreeMap 实现了Map接口,成为Map框架中的一员,可以包含着key—value形式的元素;
(3)TreeMap 实现了NavigableMap接口,意味着拥有了更强的元素搜索能力;
(4)TreeMap 实现了Cloneable接口,实现了clone()方法,可以被克隆;
(5)TreeMap 实现了Java.io.Serializable接口,支持序列化操作,可通过Hessian协议进行传输。

对于SortedMap来说,该类是TreeMap体系中的父接口,也是区别于HashMap体系最关键的一个接口。
主要原因就是SortedMap接口中定义的第一个方法—-Comparator<? super K> comparator()

  1. public interface SortedMap<K,V> extends Map<K,V> {
  2. //返回元素比较器。如果是自然顺序,则返回null;
  3. Comparator<? super K> comparator();
  4. //返回从fromKey到toKey的集合:含头不含尾
  5. java.util.SortedMap<K,V> subMap(K fromKey, K toKey);
  6. //返回从头到toKey的集合:不包含toKey
  7. java.util.SortedMap<K,V> headMap(K toKey);
  8. //返回从fromKey到结尾的集合:包含fromKey
  9. java.util.SortedMap<K,V> tailMap(K fromKey);
  10. //返回集合中的第一个元素:
  11. K firstKey();
  12. //返回集合中的最后一个元素:
  13. K lastKey();
  14. //返回集合中所有key的集合:
  15. Set<K> keySet();
  16. //返回集合中所有value的集合:
  17. Collection<V> values();
  18. //返回集合中的元素映射:
  19. Set<Map.Entry<K, V>> entrySet();
  20. }

NavigableMap接口又是对SortedMap进一步的扩展:主要增加了对集合内元素的搜索获取操作,例如:返回集合中某一区间内的元素、返回小于大于某一值的元素等类似操作。

  1. public interface NavigableMap<K,V> extends SortedMap<K,V> {
  2. //返回小于key的第一个元素:
  3. Map.Entry<K,V> lowerEntry(K key);
  4. //返回小于key的第一个键:
  5. K lowerKey(K key);
  6. //返回小于等于key的第一个元素:
  7. Map.Entry<K,V> floorEntry(K key);
  8. //返回小于等于key的第一个键:
  9. K floorKey(K key);
  10. //返回大于或者等于key的第一个元素:
  11. Map.Entry<K,V> ceilingEntry(K key);
  12. //返回大于或者等于key的第一个键:
  13. K ceilingKey(K key);
  14. //返回大于key的第一个元素:
  15. Map.Entry<K,V> higherEntry(K key);
  16. //返回大于key的第一个键:
  17. K higherKey(K key);
  18. //返回集合中第一个元素:
  19. Map.Entry<K,V> firstEntry();
  20. //返回集合中最后一个元素:
  21. Map.Entry<K,V> lastEntry();
  22. //返回集合中第一个元素,并从集合中删除:
  23. Map.Entry<K,V> pollFirstEntry();
  24. //返回集合中最后一个元素,并从集合中删除:
  25. Map.Entry<K,V> pollLastEntry();
  26. //返回倒序的Map集合:
  27. java.util.NavigableMap<K,V> descendingMap();
  28. NavigableSet<K> navigableKeySet();
  29. //返回Map集合中倒序的Key组成的Set集合:
  30. NavigableSet<K> descendingKeySet();
  31. java.util.NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
  32. K toKey, boolean toInclusive);
  33. java.util.NavigableMap<K,V> headMap(K toKey, boolean inclusive);
  34. java.util.NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
  35. SortedMap<K,V> subMap(K fromKey, K toKey);
  36. SortedMap<K,V> headMap(K toKey);
  37. SortedMap<K,V> tailMap(K fromKey);
  38. }

TreeMap具有如下特点:

  • 不允许出现重复的key
  • 可以插入null键,null值
  • 可以对元素进行排序
  • 无序集合(插入和遍历顺序不一致)

    TreeMap基本操作

    1. public class TreeMapTest {
    2. public static void main(String[] agrs){
    3. //创建TreeMap对象:
    4. TreeMap<String,Integer> treeMap = new TreeMap<String,Integer>();
    5. System.out.println("初始化后,TreeMap元素个数为:" + treeMap.size());
    6. //新增元素:
    7. treeMap.put("hello",1);
    8. treeMap.put("world",2);
    9. treeMap.put("my",3);
    10. treeMap.put("name",4);
    11. treeMap.put("is",5);
    12. treeMap.put("jiaboyan",6);
    13. treeMap.put("i",6);
    14. treeMap.put("am",6);
    15. treeMap.put("a",6);
    16. treeMap.put("developer",6);
    17. System.out.println("添加元素后,TreeMap元素个数为:" + treeMap.size());
    18. //遍历元素:
    19. Set<Map.Entry<String,Integer>> entrySet = treeMap.entrySet();
    20. for(Map.Entry<String,Integer> entry : entrySet){
    21. String key = entry.getKey();
    22. Integer value = entry.getValue();
    23. System.out.println("TreeMap元素的key:"+key+",value:"+value);
    24. }
    25. //获取所有的key:
    26. Set<String> keySet = treeMap.keySet();
    27. for(String strKey:keySet){
    28. System.out.println("TreeMap集合中的key:"+strKey);
    29. }
    30. //获取所有的value:
    31. Collection<Integer> valueList = treeMap.values();
    32. for(Integer intValue:valueList){
    33. System.out.println("TreeMap集合中的value:" + intValue);
    34. }
    35. //获取元素:
    36. Integer getValue = treeMap.get("jiaboyan");//获取集合内元素key为"jiaboyan"的值
    37. String firstKey = treeMap.firstKey();//获取集合内第一个元素
    38. String lastKey =treeMap.lastKey();//获取集合内最后一个元素
    39. String lowerKey =treeMap.lowerKey("jiaboyan");//获取集合内的key小于"jiaboyan"的key
    40. String ceilingKey =treeMap.ceilingKey("jiaboyan");//获取集合内的key大于等于"jiaboyan"的key
    41. SortedMap<String,Integer> sortedMap =treeMap.subMap("a","my");//获取集合的key从"a"到"my"的元素
    42. //删除元素:
    43. Integer removeValue = treeMap.remove("jiaboyan");//删除集合中key为"jiaboyan"的元素
    44. treeMap.clear(); //清空集合元素:
    45. //判断方法:
    46. boolean isEmpty = treeMap.isEmpty();//判断集合是否为空
    47. boolean isContain = treeMap.containsKey("jiaboyan");//判断集合的key中是否包含"jiaboyan"
    48. }
    49. }

    TreeMap排序

  • 使用元素自然排序

在使用自然顺序排序时候,需要区分两种情况:一种是Jdk定义的对象,一种是我们应用自己定义的对象;

  1. public class SortedTest implements Comparable<SortedTest> {
  2. private int age;
  3. public SortedTest(int age){
  4. this.age = age;
  5. }
  6. public int getAge() {
  7. return age;
  8. }
  9. public void setAge(int age) {
  10. this.age = age;
  11. }
  12. //自定义对象,实现compareTo(T o)方法:
  13. public int compareTo(SortedTest sortedTest) {
  14. int num = this.age - sortedTest.getAge();
  15. //为0时候,两者相同:
  16. if(num==0){
  17. return 0;
  18. //大于0时,传入的参数小:
  19. }else if(num>0){
  20. return 1;
  21. //小于0时,传入的参数大:
  22. }else{
  23. return -1;
  24. }
  25. }
  26. }
  27. public class TreeMapTest {
  28. public static void main(String[] agrs){
  29. //自然顺序比较
  30. naturalSort();
  31. }
  32. //自然排序顺序:
  33. public static void naturalSort(){
  34. //第一种情况:Integer对象
  35. TreeMap<Integer,String> treeMapFirst = new TreeMap<Integer, String>();
  36. treeMapFirst.put(1,"jiaboyan");
  37. treeMapFirst.put(6,"jiaboyan");
  38. treeMapFirst.put(3,"jiaboyan");
  39. treeMapFirst.put(10,"jiaboyan");
  40. treeMapFirst.put(7,"jiaboyan");
  41. treeMapFirst.put(13,"jiaboyan");
  42. System.out.println(treeMapFirst.toString());
  43. //第二种情况:SortedTest对象
  44. TreeMap<SortedTest,String> treeMapSecond = new TreeMap<SortedTest, String>();
  45. treeMapSecond.put(new SortedTest(10),"jiaboyan");
  46. treeMapSecond.put(new SortedTest(1),"jiaboyan");
  47. treeMapSecond.put(new SortedTest(13),"jiaboyan");
  48. treeMapSecond.put(new SortedTest(4),"jiaboyan");
  49. treeMapSecond.put(new SortedTest(0),"jiaboyan");
  50. treeMapSecond.put(new SortedTest(9),"jiaboyan");
  51. System.out.println(treeMapSecond.toString());
  52. }
  53. }

在自然顺序比较中,需要让被比较的元素实现Comparable接口,否则在向集合里添加元素时报:”java.lang.ClassCastException: com.jiaboyan.collection.map.SortedTest cannot be cast to java.lang.Comparable”异常;这是因为在调用put()方法时,会将传入的元素转化成Comparable类型对象,所以当你传入的元素没有实现Comparable接口时,就无法转换,报错。

  • 使用自定义比较器排序

使用自定义比较器排序,需要在创建TreeMap对象时,将自定义比较器对象传入到TreeMap构造方法中;
自定义比较器对象,需要实现Comparator接口,并实现比较方法compare(T o1,T o2);
值得一提的是,使用自定义比较器排序的话,被比较的对象无需再实现Comparable接口了。

  1. public class SortedTest {
  2. private int age;
  3. public SortedTest(int age){
  4. this.age = age;
  5. }
  6. public int getAge() {
  7. return age;
  8. }
  9. public void setAge(int age) {
  10. this.age = age;
  11. }
  12. }
  13. public class SortedTestComparator implements Comparator<SortedTest> {
  14. //自定义比较器:实现compare(T o1,T o2)方法:
  15. public int compare(SortedTest sortedTest1, SortedTest sortedTest2) {
  16. int num = sortedTest1.getAge() - sortedTest2.getAge();
  17. if(num==0){//为0时候,两者相同:
  18. return 0;
  19. }else if(num>0){//大于0时,后面的参数小:
  20. return 1;
  21. }else{//小于0时,前面的参数小:
  22. return -1;
  23. }
  24. }
  25. }
  26. public class TreeMapTest {
  27. public static void main(String[] agrs){
  28. //自定义顺序比较
  29. customSort();
  30. }
  31. //自定义排序顺序:
  32. public static void customSort(){
  33. TreeMap<SortedTest,String> treeMap = new TreeMap<SortedTest, String>(new SortedTestComparator());
  34. treeMap.put(new SortedTest(10),"hello");
  35. treeMap.put(new SortedTest(21),"my");
  36. treeMap.put(new SortedTest(15),"name");
  37. treeMap.put(new SortedTest(2),"is");
  38. treeMap.put(new SortedTest(1),"jiaboyan");
  39. treeMap.put(new SortedTest(7),"world");
  40. System.out.println(treeMap.toString());
  41. }
  42. }

background introduction (tree)

  • 二叉查找树

二叉查找树规定:

  1. 如果二叉查找树的左子树不为空,那么它的左子树上的任意节点的值都小于根节点的值;
  2. 如果二叉查找树的右子树不为空,那么它的右子树上的任意节点的值都大于根节点的值;
  • 平衡二叉树(AVL树)

它要求左右子树的高度差的绝对值不能大于1,也就是说左右子树的高度差只能为-1、0、1。

  • 红黑树

image.png
NIL节点是就是一个假想的或是无实在意义的节点,所有应该指向NULL的指针,都看成指向了NIL节点。包括叶节点的子节点指针或是根节点的父指针(均是指向null的)。

红黑树,即红颜色和黑颜色并存的自平衡二叉查找树,插入的元素节点会被赋为红色或者黑色,待所有元素插入完成后就形成了一颗完整的红黑树。
一颗红黑树必须满足以下几点要求:

  1. 树中每个节点必须是有颜色的,要么红色,要么黑色;
  2. 树中的根节点必须是黑色的;
  3. 树中的叶节点必须是黑色的,也就是树尾的NIL节点或者为null的节点;
  4. 树中任意一个节点如果是红色的,那么它的两个子节点一定是黑色的;
  5. 任意节点到叶节点(树最下面一个节点)的每一条路径所包含的黑色节点数目一定相同;

首先,我们先来看下在红黑树中,每一个节点的数据结构。

  1. public class TreeNode {
  2. //节点的值:
  3. private int data;
  4. //节点的颜色:
  5. private String color;
  6. //指向左孩子的指针:
  7. private TreeNode leftTreeNode;
  8. //指向右孩子的指针:
  9. private TreeNode rightTreeNode;
  10. //指向父节点的指针
  11. private TreeNode parentNode;
  12. public TreeNode(int data, String color, TreeNode leftTreeNode,
  13. TreeNode rightTreeNode, TreeNode parentNode) {
  14. this.data = data;
  15. this.color = color;
  16. this.leftTreeNode = leftTreeNode;
  17. this.rightTreeNode = rightTreeNode;
  18. this.parentNode = parentNode;
  19. }
  20. public int getData() {
  21. return data;
  22. }
  23. public void setData(int data) {
  24. this.data = data;
  25. }
  26. public String getColor() {return color;}
  27. public void setColor(String color) {this.color = color;}
  28. public TreeNode getLeftTreeNode() {
  29. return leftTreeNode;
  30. }
  31. public void setLeftTreeNode(TreeNode leftTreeNode) {
  32. this.leftTreeNode = leftTreeNode;
  33. }
  34. public TreeNode getRightTreeNode() {
  35. return rightTreeNode;
  36. }
  37. public void setRightTreeNode(TreeNode rightTreeNode) {
  38. this.rightTreeNode = rightTreeNode;
  39. }
  40. public TreeNode getParentNode() {return parentNode;}
  41. public void setParentNode(TreeNode parentNode) {this.parentNode = parentNode;}
  42. }

与HashMap不同,TreeMap底层不是数组结构,成员变量中并没有数组,而是用根节点root来替代,所有的操作都是通过根节点来进行的。

  1. public class TreeMap<K,V>
  2. extends AbstractMap<K,V>
  3. implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
  4. //自定义的比较器:
  5. private final Comparator<? super K> comparator;
  6. //红黑树的根节点:
  7. private transient java.util.TreeMap.Entry<K,V> root = null;
  8. //集合元素数量:
  9. private transient int size = 0;
  10. //对TreeMap操作的数量:
  11. private transient int modCount = 0;
  12. //无参构造方法:comparator属性置为null
  13. //代表使用key的自然顺序来维持TreeMap的顺序,这里要求key必须实现Comparable接口
  14. public TreeMap() {
  15. comparator = null;
  16. }
  17. //带有比较器的构造方法:初始化comparator属性;
  18. public TreeMap(Comparator<? super K> comparator) {
  19. this.comparator = comparator;
  20. }
  21. //带有map的构造方法:
  22. //同样比较器comparator为空,使用key的自然顺序排序
  23. public TreeMap(Map<? extends K, ? extends V> m) {
  24. comparator = null;
  25. putAll(m);
  26. }
  27. //带有SortedMap的构造方法:
  28. //根据SortedMap的比较器来来维持TreeMap的顺序
  29. public TreeMap(SortedMap<K, ? extends V> m) {
  30. comparator = m.comparator();
  31. try {
  32. buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
  33. } catch (java.io.IOException cannotHappen) {
  34. } catch (ClassNotFoundException cannotHappen) {
  35. }
  36. }
  37. }

TreeMap元素添加

  1. //插入key-value:
  2. public V put(K key, V value) {
  3. //根节点赋值给t:
  4. java.util.TreeMap.Entry<K,V> t = root;
  5. //如果根节点为null,则创建第一个节点,根节点
  6. if (t == null) {
  7. //对传入的元素进行比较,若TreeMap没定义了Comparator,则验证传入的元素是否实现了Comparable接口;
  8. compare(key, key);
  9. //根节点赋值:
  10. root = new java.util.TreeMap.Entry<>(key, value, null);
  11. //长度为1:
  12. size = 1;
  13. //修改次数+1
  14. modCount++;
  15. //直接返回:此时根节点默认为黑色
  16. return null;
  17. }
  18. //如果根节点不为null:
  19. int cmp;
  20. java.util.TreeMap.Entry<K,V> parent;
  21. Comparator<? super K> cpr = comparator;
  22. //判断TreeMap中自定义比较器comparator是否为null:
  23. if (cpr != null) {
  24. // do while循环,查找新插入节点的父节点:
  25. do {
  26. // 记录上次循环的节点t,首先将根节点赋值给parent:
  27. parent = t;
  28. //利用自定义比较器的比较方法:传入的key跟当前遍历节点比较:
  29. cmp = cpr.compare(key, t.key);
  30. //判断结果小于0,处于父节点的左边
  31. if (cmp < 0)
  32. t = t.left;
  33. else if (cmp > 0)
  34. //判断结果大于0,处于父节点的右边
  35. t = t.right;
  36. else
  37. //判断结果等于0,为当前节点,覆盖原有节点处的value:
  38. return t.setValue(value);
  39. // 只有当t为null,也就是找到了新节点的parent了
  40. } while (t != null);
  41. } else {
  42. //没有自定义比较器:
  43. if (key == null)
  44. //TreeMap不允许插入key为null,抛异常:
  45. throw new NullPointerException();
  46. //将key转换为Comparable对象:若key没有实现Comparable接口,此处会报错
  47. Comparable<? super K> k = (Comparable<? super K>) key;
  48. //同上:寻找新增节点的父节点:
  49. do {
  50. parent = t;
  51. cmp = k.compareTo(t.key);
  52. if (cmp < 0)
  53. t = t.left;
  54. else if (cmp > 0)
  55. t = t.right;
  56. else
  57. return t.setValue(value);
  58. } while (t != null);
  59. }
  60. //构造新增节点对象:
  61. java.util.TreeMap.Entry<K,V> e = new java.util.TreeMap.Entry<>(key, value, parent);
  62. //根据之前的比较结果,判断新增节点是在父节点的左边还是右边
  63. if (cmp < 0)
  64. // 如果新节点key的值小于父节点key的值,则插在父节点的左侧
  65. parent.left = e;
  66. else
  67. // 如果新节点key的值大于父节点key的值,则插在父节点的右侧
  68. parent.right = e;
  69. //核心方法:插入新的节点后,为了保持红黑树平衡,对红黑树进行调整
  70. fixAfterInsertion(e);
  71. size++;
  72. modCount++;
  73. return null;
  74. }
  75. //对插入的元素比较:若TreeMap没有自定义比较器,则调用调用默认自然顺序比较,要求元素必须实现Comparable接口;
  76. //若自定义比较器,则用自定义比较器对元素进行比较;
  77. final int compare(Object k1, Object k2) {
  78. return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
  79. : comparator.compare((K)k1, (K)k2);
  80. }
  81. //核心方法:维护TreeMap平衡的处理逻辑;(回顾上面的图文描述)
  82. private void fixAfterInsertion(java.util.TreeMap.Entry<K,V> x) {
  83. //首先将新插入节点的颜色设置为红色
  84. x.color = RED;
  85. //TreeMap是否平衡的重要判断,当不在满足循环条件时,代表树已经平衡;
  86. //x不为null,不是根节点,父节点是红色(三者均满足才进行维护):
  87. while (x != null && x != root && x.parent.color == RED) {
  88. //节点x的父节点 是 x祖父节点的左孩子:
  89. if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
  90. //获取x节点的叔叔节点y:
  91. java.util.TreeMap.Entry<K,V> y = rightOf(parentOf(parentOf(x)));
  92. //叔叔节点y是红色:
  93. if (colorOf(y) == RED) {
  94. //将x的父节点设置黑色:
  95. setColor(parentOf(x), BLACK);
  96. //将x的叔叔节点y设置成黑色:
  97. setColor(y, BLACK);
  98. //将x的祖父节点设置成红色:
  99. setColor(parentOf(parentOf(x)), RED);
  100. //将x节点的祖父节点设置成x(进入了下一次判断):
  101. x = parentOf(parentOf(x));
  102. } else {
  103. //叔叔节点y不为红色:
  104. //x为其父节点的右孩子:
  105. if (x == rightOf(parentOf(x))) {
  106. x = parentOf(x);
  107. rotateLeft(x);
  108. }
  109. setColor(parentOf(x), BLACK);
  110. setColor(parentOf(parentOf(x)), RED);
  111. //右旋:
  112. rotateRight(parentOf(parentOf(x)));
  113. } } else {
  114. //节点x的父节点 是x祖父节点的右孩子:
  115. //获取x节点的叔叔节点y:
  116. java.util.TreeMap.Entry<K,V> y = leftOf(parentOf(parentOf(x)));
  117. //判断叔叔节点y是否为红色:
  118. if (colorOf(y) == RED) {
  119. setColor(parentOf(x), BLACK);12
  120. setColor(y, BLACK);5
  121. setColor(parentOf(parentOf(x)), RED);10
  122. x = parentOf(parentOf(x));
  123. } else {
  124. if (x == leftOf(parentOf(x))) {
  125. x = parentOf(x);
  126. rotateRight(x);
  127. }
  128. setColor(parentOf(x), BLACK);
  129. setColor(parentOf(parentOf(x)), RED);
  130. //左旋:
  131. rotateLeft(parentOf(parentOf(x)));
  132. }
  133. }
  134. }
  135. root.color = BLACK;
  136. }
  137. //获取节点的颜色:
  138. private static <K,V> boolean colorOf(java.util.TreeMap.Entry<K,V> p) {
  139. //节点为null,则默认为黑色;
  140. return (p == null ? BLACK : p.color);
  141. }
  142. //获取p节点的父节点:
  143. private static <K,V> java.util.TreeMap.Entry<K,V> parentOf(java.util.TreeMap.Entry<K,V> p) {
  144. return (p == null ? null: p.parent);
  145. }
  146. //对节点进行着色,TreeMap使用了boolean类型来代表颜色(true为红色,false为黑色)
  147. private static <K,V> void setColor(java.util.TreeMap.Entry<K,V> p, boolean c){
  148. if (p != null)
  149. p.color = c;
  150. }
  151. //获取左子节点:
  152. private static <K,V> java.util.TreeMap.Entry<K,V> leftOf(java.util.TreeMap.Entry<K,V> p) {
  153. return (p == null) ? null: p.left;
  154. }
  155. //获取右子节点:
  156. private static <K,V> java.util.TreeMap.Entry<K,V> rightOf(java.util.TreeMap.Entry<K,V> p) {
  157. return (p == null) ? null: p.right;
  158. }
  159. //左旋:
  160. private void rotateLeft(java.util.TreeMap.Entry<K,V> p) {
  161. if (p != null) {
  162. java.util.TreeMap.Entry<K,V> r = p.right;
  163. p.right = r.left;
  164. if (r.left != null)
  165. r.left.parent = p;
  166. r.parent = p.parent;
  167. if (p.parent == null)
  168. root = r;
  169. else if (p.parent.left == p)
  170. p.parent.left = r;
  171. else
  172. p.parent.right = r;
  173. r.left = p;
  174. p.parent = r;
  175. }
  176. }
  177. //右旋:
  178. private void rotateRight(java.util.TreeMap.Entry<K,V> p) {
  179. if (p != null) {
  180. java.util.TreeMap.Entry<K,V> l = p.left;
  181. p.left = l.right;
  182. if (l.right != null) l.right.parent = p;
  183. l.parent = p.parent;
  184. if (p.parent == null)
  185. root = l;
  186. else if (p.parent.right == p)
  187. p.parent.right = l;
  188. else p.parent.left = l;
  189. l.right = p;
  190. p.parent = l;
  191. }
  192. }

WeakHashMap

image.png
WeakHashMap也是一个散列表,它存储的内容是键值对(key-value)映射,而且键和值都可以为null。与其他散列表不同的是,WeakHashMap的键是弱键。即在WeakHashMap中当某个键不再正常使用时,会被从WeakHashMap中自动移除。更精确的说,对于给定的一个键,其映射的存在并不影响垃圾回收器对该键的回收,这就使得该键是可终止的。被终止,然后被回收。当一个键被终止时,它对应的键值对也就从映射中移除了。

构造方法

  1. /** 使用指定的容量、加载因子初始化WeakHashMap*/
  2. public WeakHashMap(int initialCapacity, float loadFactor) {
  3. if (initialCapacity < 0)
  4. throw new IllegalArgumentException("Illegal Initial Capacity: "+
  5. initialCapacity);
  6. if (initialCapacity > MAXIMUM_CAPACITY)
  7. initialCapacity = MAXIMUM_CAPACITY;
  8. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  9. throw new IllegalArgumentException("Illegal Load factor: "+
  10. loadFactor);
  11. int capacity = 1;
  12. while (capacity < initialCapacity)
  13. capacity <<= 1;
  14. table = new Entry[capacity];
  15. this.loadFactor = loadFactor;
  16. threshold = (int)(capacity * loadFactor);
  17. }
  18. /** 使用指定初始容量、默认加载因子创建WeakHashMap*/
  19. public WeakHashMap(int initialCapacity) {
  20. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  21. }
  22. /**使用默认初始容量 16、默认加载因子0.75创建WeakHashMap*/
  23. public WeakHashMap() {
  24. this.loadFactor = DEFAULT_LOAD_FACTOR;
  25. threshold = (int)(DEFAULT_INITIAL_CAPACITY);
  26. table = new Entry[DEFAULT_INITIAL_CAPACITY];
  27. }
  28. /** 创建包含指定传入Map的所有键值对创建WeakHashMap、使用默认加载因子、使用处理后的容量*/
  29. public WeakHashMap(Map<? extends K, ? extends V> m) {
  30. this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, 16),
  31. DEFAULT_LOAD_FACTOR);
  32. putAll(m);
  33. }

成员属性

  1. public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
  2. /** 初始化HashMap时默认的容量、必须是2的幂*/
  3. private static final int DEFAULT_INITIAL_CAPACITY = 16;
  4. /** HashMap容量最大值、必须是2幂、并且要小于2的30次方、如果容量超过这个值、将会被这个值代替*/
  5. private static final int MAXIMUM_CAPACITY = 1 << 30;
  6. /** 默认加载因子*/
  7. private static final float DEFAULT_LOAD_FACTOR = 0.75f;
  8. /** 存储数据的Entry数组,长度是2的幂。Entry的本质是一个单向链表*/
  9. private Entry[] table;
  10. /** 当前HashMap中键值对的总数*/
  11. private int size;
  12. /** 阈值*/
  13. private int threshold;
  14. /** 加载因子*/
  15. private final float loadFactor;
  16. /** 当WeakHashMap中某个“弱引用的key”由于没有再被引用而被GC收回时、被回收的“弱引用key”会被添加到"ReferenceQueue(queue)"中。
  17. * 在这里结合WeakReference使用、用于记录WeakHashMap中的弱引用键
  18. */
  19. private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
  20. private volatile int modCount;

内部方法

  1. /** 当key为null时使用的值
  2. * 因为WeakHashMap中允许“null的key”,若直接插入“null的key”,将其当作弱引用时,会被删除。
  3. */
  4. private static final Object NULL_KEY = new Object();
  5. /** 当key为null时使用的值特殊处理、将其使用静态不可变常量“new Object()”代替
  6. * 在put中会被调用、防止将null作为的key被当作“弱引用键”被GC回收。
  7. */
  8. private static Object maskNull(Object key) {
  9. return (key == null ? NULL_KEY : key);
  10. }
  11. /**
  12. * 还原对“null的key”的特殊处理
  13. * 在get(key)中被调用、返回key为null的value。
  14. */
  15. private static <K> K unmaskNull(Object key) {
  16. return (K) (key == NULL_KEY ? null : key);
  17. }

方法

  1. private Entry[] getTable() {
  2. expungeStaleEntries();
  3. return table;
  4. }
  5. public int size() {
  6. if (size == 0)
  7. return 0;
  8. expungeStaleEntries();
  9. return size;
  10. }
  11. /** 判断当前HashMap是否为空*/
  12. public boolean isEmpty() {
  13. return size() == 0;
  14. }
  15. //遍历queue中有的元素,如果table中有就把引用置为null
  16. private void expungeStaleEntries() {
  17. Entry<K,V> e;
  18. while ( (e = (Entry<K,V>) queue.poll()) != null) {
  19. int h = e.hash;
  20. int i = indexFor(h, table.length);
  21. Entry<K,V> prev = table[i];
  22. Entry<K,V> p = prev;
  23. while (p != null) {
  24. Entry<K,V> next = p.next;
  25. if (p == e) {
  26. if (prev == e)
  27. table[i] = next;
  28. else
  29. prev.next = next;
  30. e.next = null; // Help GC
  31. e.value = null; // " "
  32. size--;
  33. break;
  34. }
  35. prev = p;
  36. p = next;
  37. }
  38. }
  39. }
  1. /** 获取指定key对应的value*/
  2. public V get(Object key) {
  3. Object k = maskNull(key);
  4. int h = HashMap.hash(k.hashCode());
  5. Entry[] tab = getTable();
  6. int index = indexFor(h, tab.length);
  7. Entry<K,V> e = tab[index];
  8. while (e != null) {
  9. if (e.hash == h && eq(k, e.get()))
  10. return e.value;
  11. e = e.next;
  12. }
  13. return null;
  14. }
  15. /** 是否包含传入的 key*/
  16. public boolean containsKey(Object key) {
  17. return getEntry(key) != null;
  18. }
  19. /** 获取指定key所代表的映射Entry*/
  20. Entry<K,V> getEntry(Object key) {
  21. Object k = maskNull(key);
  22. int h = HashMap.hash(k.hashCode());
  23. Entry[] tab = getTable();
  24. int index = indexFor(h, tab.length);
  25. Entry<K,V> e = tab[index];
  26. while (e != null && !(e.hash == h && eq(k, e.get())))
  27. e = e.next;
  28. return e;
  29. }
  30. /** 将指定键值对放入HashMap中、如果HashMap中存在key、则替换key映射的value*/
  31. public V put(K key, V value) {
  32. K k = (K) maskNull(key);
  33. int h = HashMap.hash(k.hashCode());
  34. Entry[] tab = getTable();
  35. int i = indexFor(h, tab.length);
  36. for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
  37. if (h == e.hash && eq(k, e.get())) {
  38. V oldValue = e.value;
  39. if (value != oldValue)
  40. e.value = value;
  41. return oldValue;
  42. }
  43. }
  44. modCount++;
  45. Entry<K,V> e = tab[i];
  46. tab[i] = new Entry<K,V>(k, value, queue, h, e);
  47. if (++size >= threshold)
  48. resize(tab.length * 2);
  49. return null;
  50. }
  51. /** rehash当前WeakHashMap、此方法会在WeakHashMap容量达到阀值的时候自动调用*/
  52. void resize(int newCapacity) {
  53. Entry[] oldTable = getTable();
  54. int oldCapacity = oldTable.length;
  55. if (oldCapacity == MAXIMUM_CAPACITY) {
  56. threshold = Integer.MAX_VALUE;
  57. return;
  58. }
  59. Entry[] newTable = new Entry[newCapacity];
  60. transfer(oldTable, newTable);
  61. table = newTable;
  62. /*
  63. * If ignoring null elements and processing ref queue caused massive
  64. * shrinkage, then restore old table. This should be rare, but avoids
  65. * unbounded expansion of garbage-filled tables.
  66. */
  67. if (size >= threshold / 2) {
  68. threshold = (int)(newCapacity * loadFactor);
  69. } else {
  70. expungeStaleEntries();
  71. transfer(newTable, oldTable);
  72. table = oldTable;
  73. }
  74. }
  75. /** 将原来table中所有元素转移到新的table中*/
  76. private void transfer(Entry[] src, Entry[] dest) {
  77. for (int j = 0; j < src.length; ++j) {
  78. Entry<K,V> e = src[j];
  79. src[j] = null;
  80. while (e != null) {
  81. Entry<K,V> next = e.next;
  82. Object key = e.get();
  83. if (key == null) {
  84. e.next = null; // Help GC
  85. e.value = null; // " "
  86. size--;
  87. } else {
  88. int i = indexFor(e.hash, dest.length);
  89. e.next = dest[i];
  90. dest[i] = e;
  91. }
  92. e = next;
  93. }
  94. }
  95. }
  96. /** 将m中所有键值对存储到HashMap中*/
  97. public void putAll(Map<? extends K, ? extends V> m) {
  98. int numKeysToBeAdded = m.size();
  99. if (numKeysToBeAdded == 0)
  100. return;
  101. /*
  102. * 计算容量是否满足添加元素条件
  103. * 若不够则将原来容量扩容2倍
  104. */
  105. if (numKeysToBeAdded > threshold) {
  106. int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
  107. if (targetCapacity > MAXIMUM_CAPACITY)
  108. targetCapacity = MAXIMUM_CAPACITY;
  109. int newCapacity = table.length;
  110. while (newCapacity < targetCapacity)
  111. newCapacity <<= 1;
  112. if (newCapacity > table.length)
  113. resize(newCapacity);
  114. }
  115. //使用迭代器迭代m中每个元素、然后添加到HashMap中
  116. for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
  117. put(e.getKey(), e.getValue());
  118. }

Entry

  1. /**
  2. * Entry是单向链表。
  3. * 他继承WeakReference、使得可以使用Entry的key作为弱引用、并且向ReferenceQueue(queue)中注册该引用、以便后期检测WeakHashMap中key的引用类型、进而调整WeakHashMap
  4. * 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数
  5. */
  6. private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
  7. private V value;
  8. private final int hash;
  9. private Entry<K,V> next;
  10. /** 创建一个实体Entry、并将Entry的key以弱引用的形式向给定的ReferenceQueue注册*/
  11. Entry(K key, V value, ReferenceQueue<K> queue, int hash, Entry<K,V> next) {
  12. //创建引用给定对象的新的弱引用,并向给定队列注册该引用。
  13. super(key, queue);
  14. this.value = value;
  15. this.hash = hash;
  16. this.next = next;
  17. }
  18. }

ConcurrentHashMap

Java 7基于分段锁的ConcurrentHashMap

Java 7中的ConcurrentHashMap的底层数据结构仍然是数组和链表。与HashMap不同的是,ConcurrentHashMap最外层不是一个大的数组,而是一个Segment的数组。每个Segment包含一个与HashMap数据结构差不多的链表数组。整体数据结构如下图所示。
image.png
final Segment<K,V>[] segments;
Segment继承了ReentrantLock,所以它就是一种可重入锁(ReentrantLock)。在ConcurrentHashMap,一个Segment就是一个子哈希表,Segment里维护了一个HashEntry数组,并发环境下,对于不同Segment的数据进行操作是不用考虑锁竞争的。(就按默认的ConcurrentLevel为16来讲,理论上就允许16个线程并发执行)。
Segment类似于HashMap,一个Segment维护着一个HashEntry数组。
transient volatile HashEntry<K,V>[] table;

  1. static final class HashEntry<K,V> {
  2. final int hash;
  3. final K key;
  4. volatile V value;
  5. volatile HashEntry<K,V> next;
  6. //其他省略
  7. }

构造方法

  1. public ConcurrentHashMap(int initialCapacity,
  2. float loadFactor, int concurrencyLevel) {
  3. if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
  4. throw new IllegalArgumentException();
  5. //MAX_SEGMENTS 为1<<16=65536,也就是最大并发数为65536
  6. if (concurrencyLevel > MAX_SEGMENTS)
  7. concurrencyLevel = MAX_SEGMENTS;
  8. //2的sshif次方等于ssize,例:ssize=16,sshift=4;ssize=32,sshif=5
  9. int sshift = 0;
  10. //ssize 为segments数组长度,根据concurrentLevel计算得出
  11. int ssize = 1;
  12. while (ssize < concurrencyLevel) {
  13. ++sshift;
  14. ssize <<= 1;
  15. }
  16. //segmentShift和segmentMask这两个变量在定位segment时会用到,后面会详细讲
  17. this.segmentShift = 32 - sshift;
  18. this.segmentMask = ssize - 1;
  19. if (initialCapacity > MAXIMUM_CAPACITY)
  20. initialCapacity = MAXIMUM_CAPACITY;
  21. //计算cap的大小,即Segment中HashEntry的数组长度,cap也一定为2的n次方.
  22. int c = initialCapacity / ssize;
  23. if (c * ssize < initialCapacity)
  24. ++c;
  25. int cap = MIN_SEGMENT_TABLE_CAPACITY;
  26. while (cap < c)
  27. cap <<= 1;
  28. //创建segments数组并初始化第一个Segment,其余的Segment延迟初始化
  29. Segment<K,V> s0 =
  30. new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
  31. (HashEntry<K,V>[])new HashEntry[cap]);
  32. Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
  33. UNSAFE.putOrderedObject(ss, SBASE, s0);
  34. this.segments = ss;
  35. }

从上面的代码可以看出来,Segment数组的大小ssize是由concurrentLevel来决定的,但是却不一定等于concurrentLevel,ssize一定是大于或等于concurrentLevel的最小的2的次幂。比如:默认情况下concurrentLevel是16,则ssize为16;若concurrentLevel为14,ssize为16;若concurrentLevel为17,则ssize为32。为什么Segment的数组大小一定是2的次幂?其实主要是便于通过按位与的散列算法来定位Segment的index。

put方法

1.定位segment并确保定位的Segment已初始化 2.调用Segment的put方法。

  1. public V put(K key, V value) {
  2. Segment<K,V> s;
  3. //concurrentHashMap不允许key/value为空
  4. if (value == null)
  5. throw new NullPointerException();
  6. //hash函数对key的hashCode重新散列,避免差劲的不合理的hashcode,保证散列均匀
  7. int hash = hash(key);
  8. //返回的hash值无符号右移segmentShift位与段掩码进行位运算,定位segment
  9. int j = (hash >>> segmentShift) & segmentMask;
  10. if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
  11. (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
  12. s = ensureSegment(j);
  13. return s.put(key, hash, value, false);
  14. }
  15. //来看下concurrentHashMap代理到Segment上的put方法,
  16. //Segment中的put方法是要加锁的。只不过是锁粒度细了而已。
  17. final V put(K key, int hash, V value, boolean onlyIfAbsent) {
  18. HashEntry<K,V> node = tryLock() ? null :
  19. scanAndLockForPut(key, hash, value);
  20. //tryLock不成功时会遍历定位到的HashEnry位置的链表(遍历主要是为了使CPU缓存链表),
  21. //若找不到,则创建HashEntry。tryLock一定次数后(MAX_SCAN_RETRIES变量决定),则lock。
  22. //若遍历过程中,由于其他线程的操作导致链表头结点变化,则需要重新遍历。
  23. V oldValue;
  24. try {
  25. HashEntry<K,V>[] tab = table;
  26. int index = (tab.length - 1) & hash;
  27. //定位HashEntry,可以看到,
  28. //这个hash值在定位Segment时和在Segment中定位HashEntry都会用到,
  29. //只不过定位Segment时只用到高几位。
  30. HashEntry<K,V> first = entryAt(tab, index);
  31. for (HashEntry<K,V> e = first;;) {
  32. //分两种情况,1.entry不等于null,则遍历entry中的链表
  33. if (e != null) {
  34. K k;
  35. if ((k = e.key) == key ||
  36. (e.hash == hash && key.equals(k))) {
  37. oldValue = e.value;
  38. if (!onlyIfAbsent) {
  39. e.value = value;
  40. ++modCount;
  41. }
  42. break;
  43. }
  44. e = e.next;
  45. }
  46. //2.entry等于null,需要new,再判断node是否创建成功
  47. else {
  48. //1.node new成功就把原有数组的链表接到node后面
  49. if (node != null)
  50. node.setNext(first);
  51. else
  52. //2.node new 失败就再new hashentry
  53. node = new HashEntry<K,V>(hash, key, value, first);
  54. int c = count + 1;              
  55. //若c超出阈值threshold,需要扩容并rehash。扩容后的容量是当前容量的2倍。
  56. //这样可以最大程度避免之前散列好的entry重新散列,
  57. //具体在另一篇文章中有详细分析,不赘述。
  58. //扩容并rehash的这个过程是比较消耗资源的。
  59. if (c > threshold && tab.length < MAXIMUM_CAPACITY)
  60. rehash(node);
  61. else
  62. //不扩容就直接替换tab的链表头
  63. setEntryAt(tab, index, node);
  64. ++modCount;
  65. count = c;
  66. oldValue = null;
  67. break;
  68. }
  69. }
  70. } finally {
  71. unlock();
  72. }
  73. return oldValue;
  74. }

关于segmentShift和segmentMask
  segmentShift和segmentMask这两个全局变量的主要作用是用来定位Segment,int j =(hash >>> segmentShift) & segmentMask
  segmentMask:段掩码,假如segments数组长度为16,则段掩码为16-1=15;segments长度为32,段掩码为32-1=31。这样得到的所有bit位都为1,可以更好地保证散列的均匀性。
  segmentShift:2的sshift次方等于ssize,segmentShift=32-sshift。若segments长度为16,segmentShift=32-4=28;若segments长度为32,segmentShift=32-5=27。而计算得出的hash值最大为32位,无符号右移segmentShift,则意味着只保留高几位(其余位是没用的),然后与段掩码segmentMask位运算来定位Segment。

get方法

 get方法无需加锁,由于其中涉及到的共享变量都使用volatile修饰,volatile可以保证内存可见性,所以不会读取到过期数据。

  1. public V get(Object key) {
  2. Segment<K,V> s;
  3. HashEntry<K,V>[] tab;
  4. int h = hash(key);
  5. long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
  6. //先定位Segment,再定位HashEntry
  7. if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
  8. (tab = s.table) != null) {
  9. for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
  10. (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
  11. e != null; e = e.next) {
  12. K k;
  13. if ((k = e.key) == key || (e.hash == h && key.equals(k)))
  14. return e.value;
  15. }
  16. }
  17. return null;
  18. }