当前位置:   article > 正文

HashMap详解_hashmap key数量256

hashmap key数量256

目录

概要

第1部分 HashMap介绍

第2部分 HashMap数据结构

第3部分 HashMap源码解析(基于JDK1.6.0_45)

第4部分 HashMap遍历方式

第5部分 HashMap示例

本文转载自http://www.cnblogs.com/skywang12345/p/3310835.html。致敬原作者


概要

这一章,我们对HashMap进行学习。
我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap。内容包括:

 

第1部分 HashMap介绍

HashMap简介

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。

HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。容量 是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

加载因子是表示Hsah表中元素的填满的程度.若:加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.反之,加载因子越小,填满的元素越少,好处是:冲突的机会减小了,但:空间浪费多了.

冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小.

因此,必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的"时-空"矛盾的平衡与折衷.

HashMap的构造函数

HashMap共有4个构造函数,如下:

  1. // 默认构造函数。
  2. HashMap()
  3. // 指定“容量大小”的构造函数
  4. HashMap(int capacity)
  5. // 指定“容量大小”和“加载因子”的构造函数
  6. HashMap(int capacity, float loadFactor)
  7. // 包含“子Map”的构造函数
  8. HashMap(Map<? extends K, ? extends V> map)

 

HashMap的API:

  1. void clear()
  2. Object clone()
  3. boolean containsKey(Object key)
  4. boolean containsValue(Object value)
  5. Set<Entry<K, V>> entrySet()
  6. V get(Object key)
  7. boolean isEmpty()
  8. Set<K> keySet()
  9. V put(K key, V value)
  10. void putAll(Map<? extends K, ? extends V> map)
  11. V remove(Object key)
  12. int size()
  13. Collection<V> values()

第2部分 HashMap数据结构

HashMap的继承关系

  1. java.lang.Object
  2. ↳ java.util.AbstractMap<K, V>
  3. ↳ java.util.HashMap<K, V>
  4. public class HashMap<K,V>
  5. extends AbstractMap<K,V>
  6. implements Map<K,V>, Cloneable, Serializable { }

HashMap与Map关系如下图

从图中可以看出: 
(01) HashMap继承于AbstractMap类,实现了Map接口。Map是"key-value键值对"接口,AbstractMap实现了"键值对"的通用函数接口。 
(02) HashMap是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, size, threshold, loadFactor, modCount。
  table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。 
  size是HashMap的大小,它是HashMap保存的键值对的数量。 
  threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
  loadFactor就是加载因子。 
  modCount是用来实现fail-fast机制的。

 

第3部分 HashMap源码解析(基于JDK1.6.0_45)

为了更了解HashMap的原理,下面对HashMap源码代码作出分析。
在阅读源码时,建议参考后面的说明来建立对HashMap的整体认识,这样更容易理解HashMap。

  1. package java.util;
  2. import java.io.*;
  3. public class HashMap<K,V>
  4. extends AbstractMap<K,V>
  5. implements Map<K,V>, Cloneable, Serializable
  6. {
  7. // 默认的初始容量是16,必须是2的幂。
  8. static final int DEFAULT_INITIAL_CAPACITY = 16;
  9. // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
  10. /**HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个
  11. 实现就在把数据存到哪个链表中的算法;
  12. 这个算法实际就是取模,hash%length,计算机中直接求余效率不如位移运算,源码中做了优化hash&
  13. (length-1),
  14. hash%length==hash&(length-1)的前提是length是2的n次方;
  15. 为什么这样能均匀分布减少碰撞呢?2的n次方实际就是1后面n个0,2的n次方-1 实际就是n个1;
  16. 例如长度为9时候,3&(9-1)=0 2&(9-1)=0 ,都在0上,碰撞了;
  17. 例如长度为8时候,3&(8-1)=3 2&(8-1)=2 ,不同位置上,不碰撞;
  18. 其实就是按位“与”的时候,每一位都能 &1 ,也就是和1111……1111111进行与运算*/
  19. static final int MAXIMUM_CAPACITY = 1 << 30;
  20. // 默认加载因子
  21. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  22. // 存储数据的Entry数组,长度是2的幂。
  23. // HashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表
  24. transient Entry[] table;
  25. // HashMap的大小,它是HashMap保存的键值对的数量
  26. transient int size;
  27. // HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)
  28. int threshold;
  29. // 加载因子实际大小
  30. final float loadFactor;
  31. // HashMap被改变的次数
  32. transient volatile int modCount;
  33. // 指定“容量大小”和“加载因子”的构造函数
  34. public HashMap(int initialCapacity, float loadFactor) {
  35. if (initialCapacity < 0)
  36. throw new IllegalArgumentException("Illegal initial capacity: " +
  37. initialCapacity);
  38. // HashMap的最大容量只能是MAXIMUM_CAPACITY
  39. if (initialCapacity > MAXIMUM_CAPACITY)
  40. initialCapacity = MAXIMUM_CAPACITY;
  41. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  42. throw new IllegalArgumentException("Illegal load factor: " +
  43. loadFactor);
  44. // 找出“大于initialCapacity”的最小的2的幂
  45. int capacity = 1;
  46. while (capacity < initialCapacity)
  47. capacity <<= 1;
  48. // 设置“加载因子”
  49. this.loadFactor = loadFactor;
  50. // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
  51. threshold = (int)(capacity * loadFactor);
  52. // 创建Entry数组,用来保存数据
  53. table = new Entry[capacity];
  54. init();
  55. }
  56. // 指定“容量大小”的构造函数
  57. public HashMap(int initialCapacity) {
  58. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  59. }
  60. // 默认构造函数。
  61. public HashMap() {
  62. // 设置“加载因子”
  63. this.loadFactor = DEFAULT_LOAD_FACTOR;
  64. // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
  65. threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
  66. // 创建Entry数组,用来保存数据
  67. table = new Entry[DEFAULT_INITIAL_CAPACITY];
  68. init();
  69. }
  70. // 包含“子Map”的构造函数
  71. public HashMap(Map<? extends K, ? extends V> m) {
  72. this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
  73. DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
  74. // 将m中的全部元素逐个添加到HashMap中
  75. putAllForCreate(m);
  76. }
  77. static int hash(int h) {
  78. h ^= (h >>> 20) ^ (h >>> 12);
  79. return h ^ (h >>> 7) ^ (h >>> 4);
  80. }
  81. // 返回索引值
  82. // h & (length-1)保证返回值的小于length
  83. static int indexFor(int h, int length) {
  84. return h & (length-1);
  85. }
  86. public int size() {
  87. return size;
  88. }
  89. public boolean isEmpty() {
  90. return size == 0;
  91. }
  92. // 获取key对应的value
  93. public V get(Object key) {
  94. if (key == null)
  95. return getForNullKey();
  96. // 获取key的hash值
  97. int hash = hash(key.hashCode());
  98. // 在“该hash值对应的链表”上查找“键值等于key”的元素
  99. for (Entry<K,V> e = table[indexFor(hash, table.length)];
  100. e != null;
  101. e = e.next) {
  102. Object k;
  103. if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
  104. return e.value;
  105. }
  106. return null;
  107. }
  108. // 获取“key为null”的元素的值
  109. // HashMap将“key为null”的元素存储在table[0]位置!
  110. private V getForNullKey() {
  111. for (Entry<K,V> e = table[0]; e != null; e = e.next) {
  112. if (e.key == null)
  113. return e.value;
  114. }
  115. return null;
  116. }
  117. // HashMap是否包含key
  118. public boolean containsKey(Object key) {
  119. return getEntry(key) != null;
  120. }
  121. // 返回“键为key”的键值对
  122. final Entry<K,V> getEntry(Object key) {
  123. // 获取哈希值
  124. // HashMap将“key为null”的元素存储在table[0]位置,“key不为null”的则调用hash()计算哈希值
  125. int hash = (key == null) ? 0 : hash(key.hashCode());
  126. // 在“该hash值对应的链表”上查找“键值等于key”的元素
  127. for (Entry<K,V> e = table[indexFor(hash, table.length)];
  128. e != null;
  129. e = e.next) {
  130. Object k;
  131. if (e.hash == hash &&
  132. ((k = e.key) == key || (key != null && key.equals(k))))
  133. return e;
  134. }
  135. return null;
  136. }
  137. // 将“key-value”添加到HashMap中
  138. public V put(K key, V value) {
  139. // 若“key为null”,则将该键值对添加到table[0]中。
  140. if (key == null)
  141. return putForNullKey(value);
  142. // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
  143. int hash = hash(key.hashCode());
  144. int i = indexFor(hash, table.length);
  145. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  146. Object k;
  147. // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
  148. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  149. V oldValue = e.value;
  150. e.value = value;
  151. e.recordAccess(this);
  152. return oldValue;
  153. }
  154. }
  155. // 若“该key”对应的键值对不存在,则将“key-value”添加到table中
  156. modCount++;
  157. addEntry(hash, key, value, i);
  158. return null;
  159. }
  160. // putForNullKey()的作用是将“key为null”键值对添加到table[0]位置
  161. private V putForNullKey(V value) {
  162. for (Entry<K,V> e = table[0]; e != null; e = e.next) {
  163. if (e.key == null) {
  164. V oldValue = e.value;
  165. e.value = value;
  166. e.recordAccess(this);
  167. return oldValue;
  168. }
  169. }
  170. // 这里的完全不会被执行到!
  171. modCount++;
  172. addEntry(0, null, value, 0);
  173. return null;
  174. }
  175. // 创建HashMap对应的“添加方法”,
  176. // 它和put()不同。putForCreate()是内部方法,它被构造函数等调用,用来创建HashMap
  177. // 而put()是对外提供的往HashMap中添加元素的方法。
  178. private void putForCreate(K key, V value) {
  179. int hash = (key == null) ? 0 : hash(key.hashCode());
  180. int i = indexFor(hash, table.length);
  181. // 若该HashMap表中存在“键值等于key”的元素,则替换该元素的value值
  182. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  183. Object k;
  184. if (e.hash == hash &&
  185. ((k = e.key) == key || (key != null && key.equals(k)))) {
  186. e.value = value;
  187. return;
  188. }
  189. }
  190. // 若该HashMap表中不存在“键值等于key”的元素,则将该key-value添加到HashMap中
  191. createEntry(hash, key, value, i);
  192. }
  193. // 将“m”中的全部元素都添加到HashMap中。
  194. // 该方法被内部的构造HashMap的方法所调用。
  195. private void putAllForCreate(Map<? extends K, ? extends V> m) {
  196. // 利用迭代器将元素逐个添加到HashMap中
  197. for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
  198. Map.Entry<? extends K, ? extends V> e = i.next();
  199. putForCreate(e.getKey(), e.getValue());
  200. }
  201. }
  202. // 重新调整HashMap的大小,newCapacity是调整后的单位
  203. void resize(int newCapacity) {
  204. Entry[] oldTable = table;
  205. int oldCapacity = oldTable.length;
  206. if (oldCapacity == MAXIMUM_CAPACITY) {
  207. threshold = Integer.MAX_VALUE;
  208. return;
  209. }
  210. // 新建一个HashMap,将“旧HashMap”的全部元素添加到“新HashMap”中,
  211. // 然后,将“新HashMap”赋值给“旧HashMap”。
  212. Entry[] newTable = new Entry[newCapacity];
  213. transfer(newTable);
  214. table = newTable;
  215. threshold = (int)(newCapacity * loadFactor);
  216. }
  217. // 将HashMap中的全部元素都添加到newTable中
  218. void transfer(Entry[] newTable) {
  219. Entry[] src = table;
  220. int newCapacity = newTable.length;
  221. for (int j = 0; j < src.length; j++) {
  222. Entry<K,V> e = src[j];
  223. if (e != null) {
  224. src[j] = null;
  225. do {
  226. Entry<K,V> next = e.next;
  227. int i = indexFor(e.hash, newCapacity);
  228. e.next = newTable[i];
  229. newTable[i] = e;
  230. e = next;
  231. } while (e != null);
  232. }
  233. }
  234. }
  235. // 将"m"的全部元素都添加到HashMap中
  236. public void putAll(Map<? extends K, ? extends V> m) {
  237. // 有效性判断
  238. int numKeysToBeAdded = m.size();
  239. if (numKeysToBeAdded == 0)
  240. return;
  241. // 计算容量是否足够,
  242. // 若“当前实际容量 < 需要的容量”,则将容量x2。
  243. if (numKeysToBeAdded > threshold) {
  244. int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
  245. if (targetCapacity > MAXIMUM_CAPACITY)
  246. targetCapacity = MAXIMUM_CAPACITY;
  247. int newCapacity = table.length;
  248. while (newCapacity < targetCapacity)
  249. newCapacity <<= 1;
  250. if (newCapacity > table.length)
  251. resize(newCapacity);
  252. }
  253. // 通过迭代器,将“m”中的元素逐个添加到HashMap中。
  254. for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
  255. Map.Entry<? extends K, ? extends V> e = i.next();
  256. put(e.getKey(), e.getValue());
  257. }
  258. }
  259. // 删除“键为key”元素
  260. public V remove(Object key) {
  261. Entry<K,V> e = removeEntryForKey(key);
  262. return (e == null ? null : e.value);
  263. }
  264. // 删除“键为key”的元素
  265. final Entry<K,V> removeEntryForKey(Object key) {
  266. // 获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算
  267. int hash = (key == null) ? 0 : hash(key.hashCode());
  268. int i = indexFor(hash, table.length);
  269. Entry<K,V> prev = table[i];
  270. Entry<K,V> e = prev;
  271. // 删除链表中“键为key”的元素
  272. // 本质是“删除单向链表中的节点”
  273. while (e != null) {
  274. Entry<K,V> next = e.next;
  275. Object k;
  276. if (e.hash == hash &&
  277. ((k = e.key) == key || (key != null && key.equals(k)))) {
  278. modCount++;
  279. size--;
  280. if (prev == e)
  281. table[i] = next;
  282. else
  283. prev.next = next;
  284. e.recordRemoval(this);
  285. return e;
  286. }
  287. prev = e;
  288. e = next;
  289. }
  290. return e;
  291. }
  292. // 删除“键值对”
  293. final Entry<K,V> removeMapping(Object o) {
  294. if (!(o instanceof Map.Entry))
  295. return null;
  296. Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
  297. Object key = entry.getKey();
  298. int hash = (key == null) ? 0 : hash(key.hashCode());
  299. int i = indexFor(hash, table.length);
  300. Entry<K,V> prev = table[i];
  301. Entry<K,V> e = prev;
  302. // 删除链表中的“键值对e”
  303. // 本质是“删除单向链表中的节点”
  304. while (e != null) {
  305. Entry<K,V> next = e.next;
  306. if (e.hash == hash && e.equals(entry)) {
  307. modCount++;
  308. size--;
  309. if (prev == e)
  310. table[i] = next;
  311. else
  312. prev.next = next;
  313. e.recordRemoval(this);
  314. return e;
  315. }
  316. prev = e;
  317. e = next;
  318. }
  319. return e;
  320. }
  321. // 清空HashMap,将所有的元素设为null
  322. public void clear() {
  323. modCount++;
  324. Entry[] tab = table;
  325. for (int i = 0; i < tab.length; i++)
  326. tab[i] = null;
  327. size = 0;
  328. }
  329. // 是否包含“值为value”的元素
  330. public boolean containsValue(Object value) {
  331. // 若“value为null”,则调用containsNullValue()查找
  332. if (value == null)
  333. return containsNullValue();
  334. // 若“value不为null”,则查找HashMap中是否有值为value的节点。
  335. Entry[] tab = table;
  336. for (int i = 0; i < tab.length ; i++)
  337. for (Entry e = tab[i] ; e != null ; e = e.next)
  338. if (value.equals(e.value))
  339. return true;
  340. return false;
  341. }
  342. // 是否包含null值
  343. private boolean containsNullValue() {
  344. Entry[] tab = table;
  345. for (int i = 0; i < tab.length ; i++)
  346. for (Entry e = tab[i] ; e != null ; e = e.next)
  347. if (e.value == null)
  348. return true;
  349. return false;
  350. }
  351. // 克隆一个HashMap,并返回Object对象
  352. public Object clone() {
  353. HashMap<K,V> result = null;
  354. try {
  355. result = (HashMap<K,V>)super.clone();
  356. } catch (CloneNotSupportedException e) {
  357. // assert false;
  358. }
  359. result.table = new Entry[table.length];
  360. result.entrySet = null;
  361. result.modCount = 0;
  362. result.size = 0;
  363. result.init();
  364. // 调用putAllForCreate()将全部元素添加到HashMap中
  365. result.putAllForCreate(this);
  366. return result;
  367. }
  368. // Entry是单向链表。
  369. // 它是 “HashMap链式存储法”对应的链表。
  370. // 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数
  371. static class Entry<K,V> implements Map.Entry<K,V> {
  372. final K key;
  373. V value;
  374. // 指向下一个节点
  375. Entry<K,V> next;
  376. final int hash;
  377. // 构造函数。
  378. // 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"
  379. Entry(int h, K k, V v, Entry<K,V> n) {
  380. value = v;
  381. next = n;
  382. key = k;
  383. hash = h;
  384. }
  385. public final K getKey() {
  386. return key;
  387. }
  388. public final V getValue() {
  389. return value;
  390. }
  391. public final V setValue(V newValue) {
  392. V oldValue = value;
  393. value = newValue;
  394. return oldValue;
  395. }
  396. // 判断两个Entry是否相等
  397. // 若两个Entry的“key”和“value”都相等,则返回true。
  398. // 否则,返回false
  399. public final boolean equals(Object o) {
  400. if (!(o instanceof Map.Entry))
  401. return false;
  402. Map.Entry e = (Map.Entry)o;
  403. Object k1 = getKey();
  404. Object k2 = e.getKey();
  405. if (k1 == k2 || (k1 != null && k1.equals(k2))) {
  406. Object v1 = getValue();
  407. Object v2 = e.getValue();
  408. if (v1 == v2 || (v1 != null && v1.equals(v2)))
  409. return true;
  410. }
  411. return false;
  412. }
  413. // 实现hashCode()
  414. public final int hashCode() {
  415. return (key==null ? 0 : key.hashCode()) ^
  416. (value==null ? 0 : value.hashCode());
  417. }
  418. public final String toString() {
  419. return getKey() + "=" + getValue();
  420. }
  421. // 当向HashMap中添加元素时,绘调用recordAccess()。
  422. // 这里不做任何处理
  423. void recordAccess(HashMap<K,V> m) {
  424. }
  425. // 当从HashMap中删除元素时,绘调用recordRemoval()。
  426. // 这里不做任何处理
  427. void recordRemoval(HashMap<K,V> m) {
  428. }
  429. }
  430. // 新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。
  431. void addEntry(int hash, K key, V value, int bucketIndex) {
  432. // 保存“bucketIndex”位置的值到“e”中
  433. Entry<K,V> e = table[bucketIndex];
  434. // 设置“bucketIndex”位置的元素为“新Entry”,
  435. // 设置“e”为“新Entry的下一个节点”
  436. table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
  437. // 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小
  438. if (size++ >= threshold)
  439. resize(2 * table.length);
  440. }
  441. // 创建Entry。将“key-value”插入指定位置,bucketIndex是位置索引。
  442. // 它和addEntry的区别是:
  443. // (01) addEntry()一般用在 新增Entry可能导致“HashMap的实际容量”超过“阈值”的情况下。
  444. // 例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;
  445. // put()是通过addEntry()新增Entry的。
  446. // 在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;
  447. // 因此,需要调用addEntry()
  448. // (02) createEntry() 一般用在 新增Entry不会导致“HashMap的实际容量”超过“阈值”的情况下。
  449. // 例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;
  450. // 但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中
  451. // 的全部元素添加到HashMap中,都不会超过HashMap的阈值”。
  452. // 此时,调用createEntry()即可。
  453. void createEntry(int hash, K key, V value, int bucketIndex) {
  454. // 保存“bucketIndex”位置的值到“e”中
  455. Entry<K,V> e = table[bucketIndex];
  456. // 设置“bucketIndex”位置的元素为“新Entry”,
  457. // 设置“e”为“新Entry的下一个节点”
  458. table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
  459. size++;
  460. }
  461. // HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。
  462. // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。
  463. private abstract class HashIterator<E> implements Iterator<E> {
  464. // 下一个元素
  465. Entry<K,V> next;
  466. // expectedModCount用于实现fast-fail机制。
  467. int expectedModCount;
  468. // 当前索引
  469. int index;
  470. // 当前元素
  471. Entry<K,V> current;
  472. HashIterator() {
  473. expectedModCount = modCount;
  474. if (size > 0) { // advance to first entry
  475. Entry[] t = table;
  476. // 将next指向table中第一个不为null的元素。
  477. // 这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环。
  478. while (index < t.length && (next = t[index++]) == null)
  479. ;
  480. }
  481. }
  482. public final boolean hasNext() {
  483. return next != null;
  484. }
  485. // 获取下一个元素
  486. final Entry<K,V> nextEntry() {
  487. if (modCount != expectedModCount)
  488. throw new ConcurrentModificationException();
  489. Entry<K,V> e = next;
  490. if (e == null)
  491. throw new NoSuchElementException();
  492. // 注意!!!
  493. // 一个Entry就是一个单向链表
  494. // 若该Entry的下一个节点不为空,就将next指向下一个节点;
  495. // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。
  496. if ((next = e.next) == null) {
  497. Entry[] t = table;
  498. while (index < t.length && (next = t[index++]) == null)
  499. ;
  500. }
  501. current = e;
  502. return e;
  503. }
  504. // 删除当前元素
  505. public void remove() {
  506. if (current == null)
  507. throw new IllegalStateException();
  508. if (modCount != expectedModCount)
  509. throw new ConcurrentModificationException();
  510. Object k = current.key;
  511. current = null;
  512. HashMap.this.removeEntryForKey(k);
  513. expectedModCount = modCount;
  514. }
  515. }
  516. // value的迭代器
  517. private final class ValueIterator extends HashIterator<V> {
  518. public V next() {
  519. return nextEntry().value;
  520. }
  521. }
  522. // key的迭代器
  523. private final class KeyIterator extends HashIterator<K> {
  524. public K next() {
  525. return nextEntry().getKey();
  526. }
  527. }
  528. // Entry的迭代器
  529. private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
  530. public Map.Entry<K,V> next() {
  531. return nextEntry();
  532. }
  533. }
  534. // 返回一个“key迭代器”
  535. Iterator<K> newKeyIterator() {
  536. return new KeyIterator();
  537. }
  538. // 返回一个“value迭代器”
  539. Iterator<V> newValueIterator() {
  540. return new ValueIterator();
  541. }
  542. // 返回一个“entry迭代器”
  543. Iterator<Map.Entry<K,V>> newEntryIterator() {
  544. return new EntryIterator();
  545. }
  546. // HashMap的Entry对应的集合
  547. private transient Set<Map.Entry<K,V>> entrySet = null;
  548. // 返回“key的集合”,实际上返回一个“KeySet对象”
  549. public Set<K> keySet() {
  550. Set<K> ks = keySet;
  551. return (ks != null ? ks : (keySet = new KeySet()));
  552. }
  553. // Key对应的集合
  554. // KeySet继承于AbstractSet,说明该集合中没有重复的Key。
  555. private final class KeySet extends AbstractSet<K> {
  556. public Iterator<K> iterator() {
  557. return newKeyIterator();
  558. }
  559. public int size() {
  560. return size;
  561. }
  562. public boolean contains(Object o) {
  563. return containsKey(o);
  564. }
  565. public boolean remove(Object o) {
  566. return HashMap.this.removeEntryForKey(o) != null;
  567. }
  568. public void clear() {
  569. HashMap.this.clear();
  570. }
  571. }
  572. // 返回“value集合”,实际上返回的是一个Values对象
  573. public Collection<V> values() {
  574. Collection<V> vs = values;
  575. return (vs != null ? vs : (values = new Values()));
  576. }
  577. // “value集合”
  578. // Values继承于AbstractCollection,不同于“KeySet继承于AbstractSet”,
  579. // Values中的元素能够重复。因为不同的key可以指向相同的value。
  580. private final class Values extends AbstractCollection<V> {
  581. public Iterator<V> iterator() {
  582. return newValueIterator();
  583. }
  584. public int size() {
  585. return size;
  586. }
  587. public boolean contains(Object o) {
  588. return containsValue(o);
  589. }
  590. public void clear() {
  591. HashMap.this.clear();
  592. }
  593. }
  594. // 返回“HashMap的Entry集合”
  595. public Set<Map.Entry<K,V>> entrySet() {
  596. return entrySet0();
  597. }
  598. // 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象
  599. private Set<Map.Entry<K,V>> entrySet0() {
  600. Set<Map.Entry<K,V>> es = entrySet;
  601. return es != null ? es : (entrySet = new EntrySet());
  602. }
  603. // EntrySet对应的集合
  604. // EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。
  605. private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
  606. public Iterator<Map.Entry<K,V>> iterator() {
  607. return newEntryIterator();
  608. }
  609. public boolean contains(Object o) {
  610. if (!(o instanceof Map.Entry))
  611. return false;
  612. Map.Entry<K,V> e = (Map.Entry<K,V>) o;
  613. Entry<K,V> candidate = getEntry(e.getKey());
  614. return candidate != null && candidate.equals(e);
  615. }
  616. public boolean remove(Object o) {
  617. return removeMapping(o) != null;
  618. }
  619. public int size() {
  620. return size;
  621. }
  622. public void clear() {
  623. HashMap.this.clear();
  624. }
  625. }
  626. // java.io.Serializable的写入函数
  627. // 将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中
  628. private void writeObject(java.io.ObjectOutputStream s)
  629. throws IOException
  630. {
  631. Iterator<Map.Entry<K,V>> i =
  632. (size > 0) ? entrySet0().iterator() : null;
  633. // Write out the threshold, loadfactor, and any hidden stuff
  634. s.defaultWriteObject();
  635. // Write out number of buckets
  636. s.writeInt(table.length);
  637. // Write out size (number of Mappings)
  638. s.writeInt(size);
  639. // Write out keys and values (alternating)
  640. if (i != null) {
  641. while (i.hasNext()) {
  642. Map.Entry<K,V> e = i.next();
  643. s.writeObject(e.getKey());
  644. s.writeObject(e.getValue());
  645. }
  646. }
  647. }
  648. private static final long serialVersionUID = 362498820763181265L;
  649. // java.io.Serializable的读取函数:根据写入方式读出
  650. // 将HashMap的“总的容量,实际容量,所有的Entry”依次读出
  651. private void readObject(java.io.ObjectInputStream s)
  652. throws IOException, ClassNotFoundException
  653. {
  654. // Read in the threshold, loadfactor, and any hidden stuff
  655. s.defaultReadObject();
  656. // Read in number of buckets and allocate the bucket array;
  657. int numBuckets = s.readInt();
  658. table = new Entry[numBuckets];
  659. init(); // Give subclass a chance to do its thing.
  660. // Read in size (number of Mappings)
  661. int size = s.readInt();
  662. // Read the keys and values, and put the mappings in the HashMap
  663. for (int i=0; i<size; i++) {
  664. K key = (K) s.readObject();
  665. V value = (V) s.readObject();
  666. putForCreate(key, value);
  667. }
  668. }
  669. // 返回“HashMap总的容量”
  670. int capacity() { return table.length; }
  671. // 返回“HashMap的加载因子”
  672. float loadFactor() { return loadFactor; }
  673. }

说明:

在详细介绍HashMap的代码之前,我们需要了解:HashMap就是一个散列表,它是通过“拉链法”解决哈希冲突的
还需要再补充说明的一点是影响HashMap性能的有两个参数:初始容量(initialCapacity) 和加载因子(loadFactor)。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。


第3.1部分 HashMap的“拉链法”相关内容

3.1.1 HashMap数据存储数组

transient Entry[] table;

HashMap中的key-value都是存储在Entry数组中的。

3.1.2 数据节点Entry的数据结构

  1. static class Entry<K,V> implements Map.Entry<K,V> {
  2. final K key;
  3. V value;
  4. // 指向下一个节点
  5. Entry<K,V> next;
  6. final int hash;
  7. // 构造函数。
  8. // 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"
  9. Entry(int h, K k, V v, Entry<K,V> n) {
  10. value = v;
  11. next = n;
  12. key = k;
  13. hash = h;
  14. }
  15. public final K getKey() {
  16. return key;
  17. }
  18. public final V getValue() {
  19. return value;
  20. }
  21. public final V setValue(V newValue) {
  22. V oldValue = value;
  23. value = newValue;
  24. return oldValue;
  25. }
  26. // 判断两个Entry是否相等
  27. // 若两个Entry的“key”和“value”都相等,则返回true。
  28. // 否则,返回false
  29. public final boolean equals(Object o) {
  30. if (!(o instanceof Map.Entry))
  31. return false;
  32. Map.Entry e = (Map.Entry)o;
  33. Object k1 = getKey();
  34. Object k2 = e.getKey();
  35. if (k1 == k2 || (k1 != null && k1.equals(k2))) {
  36. Object v1 = getValue();
  37. Object v2 = e.getValue();
  38. if (v1 == v2 || (v1 != null && v1.equals(v2)))
  39. return true;
  40. }
  41. return false;
  42. }
  43. // 实现hashCode()
  44. public final int hashCode() {
  45. return (key==null ? 0 : key.hashCode()) ^
  46. (value==null ? 0 : value.hashCode());
  47. }
  48. public final String toString() {
  49. return getKey() + "=" + getValue();
  50. }
  51. // 当向HashMap中添加元素时,绘调用recordAccess()。
  52. // 这里不做任何处理
  53. void recordAccess(HashMap<K,V> m) {
  54. }
  55. // 当从HashMap中删除元素时,绘调用recordRemoval()。
  56. // 这里不做任何处理
  57. void recordRemoval(HashMap<K,V> m) {
  58. }
  59. }

从中,我们可以看出 Entry 实际上就是一个单向链表。这也是为什么我们说HashMap是通过拉链法解决哈希冲突的。
Entry 实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数。这些都是基本的读取/修改key、value值的函数。

 

第3.2部分 HashMap的构造函数

HashMap共包括4个构造函数

  1. // 默认构造函数。
  2. public HashMap() {
  3. // 设置“加载因子”
  4. this.loadFactor = DEFAULT_LOAD_FACTOR;
  5. // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
  6. threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
  7. // 创建Entry数组,用来保存数据
  8. table = new Entry[DEFAULT_INITIAL_CAPACITY];
  9. init();
  10. }
  11. // 指定“容量大小”和“加载因子”的构造函数
  12. public HashMap(int initialCapacity, float loadFactor) {
  13. if (initialCapacity < 0)
  14. throw new IllegalArgumentException("Illegal initial capacity: " +
  15. initialCapacity);
  16. // HashMap的最大容量只能是MAXIMUM_CAPACITY
  17. if (initialCapacity > MAXIMUM_CAPACITY)
  18. initialCapacity = MAXIMUM_CAPACITY;
  19. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  20. throw new IllegalArgumentException("Illegal load factor: " +
  21. loadFactor);
  22. // Find a power of 2 >= initialCapacity
  23. int capacity = 1;
  24. while (capacity < initialCapacity)
  25. capacity <<= 1;
  26. // 设置“加载因子”
  27. this.loadFactor = loadFactor;
  28. // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
  29. threshold = (int)(capacity * loadFactor);
  30. // 创建Entry数组,用来保存数据
  31. table = new Entry[capacity];
  32. init();
  33. }
  34. // 指定“容量大小”的构造函数
  35. public HashMap(int initialCapacity) {
  36. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  37. }
  38. // 包含“子Map”的构造函数
  39. public HashMap(Map<? extends K, ? extends V> m) {
  40. this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
  41. DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
  42. // 将m中的全部元素逐个添加到HashMap中
  43. putAllForCreate(m);
  44. }

第3.3部分 HashMap的主要对外接口

3.3.1 clear()

clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。

  1. public void clear() {
  2. modCount++;
  3. Entry[] tab = table;
  4. for (int i = 0; i < tab.length; i++)
  5. tab[i] = null;
  6. size = 0;
  7. }

3.3.2 containsKey()

containsKey() 的作用是判断HashMap是否包含key

  1. public boolean containsKey(Object key) {
  2. return getEntry(key) != null;
  3. }

containsKey() 首先通过getEntry(key)获取key对应的Entry,然后判断该Entry是否为null
getEntry()的源码如下:

  1. final Entry<K,V> getEntry(Object key) {
  2. // 获取哈希值
  3. // HashMap将“key为null”的元素存储在table[0]位置,“key不为null”的则调用hash()计算哈希值
  4. int hash = (key == null) ? 0 : hash(key.hashCode());
  5. // 在“该hash值对应的链表”上查找“键值等于key”的元素
  6. for (Entry<K,V> e = table[indexFor(hash, table.length)];
  7. e != null;
  8. e = e.next) {
  9. Object k;
  10. if (e.hash == hash &&
  11. ((k = e.key) == key || (key != null && key.equals(k))))
  12. return e;
  13. }
  14. return null;
  15. }

getEntry() 的作用就是返回“键为key”的键值对,它的实现源码中已经进行了说明。
这里需要强调的是:HashMap将“key为null”的元素都放在table的位置0处,即table[0]中;“key不为null”的放在table的其余位置!


3.3.3 containsValue()

containsValue() 的作用是判断HashMap是否包含“值为value”的元素

  1. public boolean containsValue(Object value) {
  2. // 若“value为null”,则调用containsNullValue()查找
  3. if (value == null)
  4. return containsNullValue();
  5. // 若“value不为null”,则查找HashMap中是否有值为value的节点。
  6. Entry[] tab = table;
  7. for (int i = 0; i < tab.length ; i++)
  8. for (Entry e = tab[i] ; e != null ; e = e.next)
  9. if (value.equals(e.value))
  10. return true;
  11. return false;
  12. }

从中,我们可以看出containsNullValue()分为两步进行处理:第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。

containsNullValue() 的作用判断HashMap中是否包含“值为null”的元素

  1. private boolean containsNullValue() {
  2. Entry[] tab = table;
  3. for (int i = 0; i < tab.length ; i++)
  4. for (Entry e = tab[i] ; e != null ; e = e.next)
  5. if (e.value == null)
  6. return true;
  7. return false;
  8. }

3.3.4 entrySet()、values()、keySet()

它们3个的原理类似,这里以entrySet()为例来说明。
entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一个集合。实现代码如下:

  1. // 返回“HashMap的Entry集合”
  2. public Set<Map.Entry<K,V>> entrySet() {
  3. return entrySet0();
  4. }
  5. // 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象
  6. private Set<Map.Entry<K,V>> entrySet0() {
  7. Set<Map.Entry<K,V>> es = entrySet;
  8. return es != null ? es : (entrySet = new EntrySet());
  9. }
  10. // EntrySet对应的集合
  11. // EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。
  12. private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
  13. public Iterator<Map.Entry<K,V>> iterator() {
  14. return newEntryIterator();
  15. }
  16. public boolean contains(Object o) {
  17. if (!(o instanceof Map.Entry))
  18. return false;
  19. Map.Entry<K,V> e = (Map.Entry<K,V>) o;
  20. Entry<K,V> candidate = getEntry(e.getKey());
  21. return candidate != null && candidate.equals(e);
  22. }
  23. public boolean remove(Object o) {
  24. return removeMapping(o) != null;
  25. }
  26. public int size() {
  27. return size;
  28. }
  29. public void clear() {
  30. HashMap.this.clear();
  31. }
  32. }

HashMap是通过拉链法实现的散列表。表现在HashMap包括许多的Entry,而每一个Entry本质上又是一个单向链表。那么HashMap遍历key-value键值对的时候,是如何逐个去遍历的呢?


下面我们就看看HashMap是如何通过entrySet()遍历的。
entrySet()实际上是通过newEntryIterator()实现的。 下面我们看看它的代码:

  1. // 返回一个“entry迭代器”
  2. Iterator<Map.Entry<K,V>> newEntryIterator() {
  3. return new EntryIterator();
  4. }
  5. // Entry的迭代器
  6. private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
  7. public Map.Entry<K,V> next() {
  8. return nextEntry();
  9. }
  10. }
  11. // HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。
  12. // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。
  13. private abstract class HashIterator<E> implements Iterator<E> {
  14. // 下一个元素
  15. Entry<K,V> next;
  16. // expectedModCount用于实现fast-fail机制。
  17. int expectedModCount;
  18. // 当前索引
  19. int index;
  20. // 当前元素
  21. Entry<K,V> current;
  22. HashIterator() {
  23. expectedModCount = modCount;
  24. if (size > 0) { // advance to first entry
  25. Entry[] t = table;
  26. // 将next指向table中第一个不为null的元素。
  27. // 这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环。
  28. while (index < t.length && (next = t[index++]) == null)
  29. ;
  30. }
  31. }
  32. public final boolean hasNext() {
  33. return next != null;
  34. }
  35. // 获取下一个元素
  36. final Entry<K,V> nextEntry() {
  37. if (modCount != expectedModCount)
  38. throw new ConcurrentModificationException();
  39. Entry<K,V> e = next;
  40. if (e == null)
  41. throw new NoSuchElementException();
  42. // 注意!!!
  43. // 一个Entry就是一个单向链表
  44. // 若该Entry的下一个节点不为空,就将next指向下一个节点;
  45. // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。
  46. if ((next = e.next) == null) {
  47. Entry[] t = table;
  48. while (index < t.length && (next = t[index++]) == null)
  49. ;
  50. }
  51. current = e;
  52. return e;
  53. }
  54. // 删除当前元素
  55. public void remove() {
  56. if (current == null)
  57. throw new IllegalStateException();
  58. if (modCount != expectedModCount)
  59. throw new ConcurrentModificationException();
  60. Object k = current.key;
  61. current = null;
  62. HashMap.this.removeEntryForKey(k);
  63. expectedModCount = modCount;
  64. }
  65. }

当我们通过entrySet()获取到的Iterator的next()方法去遍历HashMap时,实际上调用的是 nextEntry() 。而nextEntry()的实现方式,先遍历Entry(根据Entry在table中的序号,从小到大的遍历);然后对每个Entry(即每个单向链表),逐个遍历。


3.3.5 get()

get() 的作用是获取key对应的value,它的实现代码如下:

  1. public V get(Object key) {
  2. if (key == null)
  3. return getForNullKey();
  4. // 获取key的hash值
  5. int hash = hash(key.hashCode());
  6. // 在“该hash值对应的链表”上查找“键值等于key”的元素
  7. for (Entry<K,V> e = table[indexFor(hash, table.length)];
  8. e != null;
  9. e = e.next) {
  10. Object k;
  11. if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
  12. return e.value;
  13. }
  14. return null;
  15. }

3.3.6 put()

put() 的作用是对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中

  1. public V put(K key, V value) {
  2. // 若“key为null”,则将该键值对添加到table[0]中。
  3. if (key == null)
  4. return putForNullKey(value);
  5. // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
  6. int hash = hash(key.hashCode());
  7. int i = indexFor(hash, table.length);
  8. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  9. Object k;
  10. // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
  11. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  12. V oldValue = e.value;
  13. e.value = value;
  14. e.recordAccess(this);
  15. return oldValue;
  16. }
  17. }
  18. // 若“该key”对应的键值对不存在,则将“key-value”添加到table中
  19. modCount++;
  20. addEntry(hash, key, value, i);
  21. return null;
  22. }

若要添加到HashMap中的键值对对应的key已经存在HashMap中,则找到该键值对;然后新的value取代旧的value,并退出!
若要添加到HashMap中的键值对对应的key不在HashMap中,则将其添加到该哈希值对应的链表中,并调用addEntry()。
下面看看addEntry()的代码:

  1. void addEntry(int hash, K key, V value, int bucketIndex) {
  2. // 保存“bucketIndex”位置的值到“e”中
  3. Entry<K,V> e = table[bucketIndex];
  4. // 设置“bucketIndex”位置的元素为“新Entry”,
  5. // 设置“e”为“新Entry的下一个节点”
  6. table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
  7. // 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小
  8. if (size++ >= threshold)
  9. resize(2 * table.length);
  10. }

addEntry() 的作用是新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。

说到addEntry(),就不得不说另一个函数createEntry()。createEntry()的代码如下:

  1. void createEntry(int hash, K key, V value, int bucketIndex) {
  2. // 保存“bucketIndex”位置的值到“e”中
  3. Entry<K,V> e = table[bucketIndex];
  4. // 设置“bucketIndex”位置的元素为“新Entry”,
  5. // 设置“e”为“新Entry的下一个节点”
  6. table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
  7. size++;
  8. }

它们的作用都是将key、value添加到HashMap中。而且,比较addEntry()和createEntry()的代码,我们发现addEntry()多了两句:

  1. if (size++ >= threshold)
  2. resize(2 * table.length);

那它们的区别到底是什么呢?
阅读代码,我们可以发现,它们的使用情景不同。
(01) addEntry()一般用在 新增Entry可能导致“HashMap的实际容量”超过“阈值”的情况下。
       例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;put()是通过addEntry()新增Entry的。
       在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;
       因此,需要调用addEntry()
(02) createEntry() 一般用在 新增Entry不会导致“HashMap的实际容量”超过“阈值”的情况下。
        例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;
       但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中的全部元素添加到HashMap中,都不会超过HashMap的阈值”。
       此时,调用createEntry()即可。

 

3.3.7 putAll()

putAll() 的作用是将"m"的全部元素都添加到HashMap中,它的代码如下:

  1. public void putAll(Map<? extends K, ? extends V> m) {
  2. // 有效性判断
  3. int numKeysToBeAdded = m.size();
  4. if (numKeysToBeAdded == 0)
  5. return;
  6. // 计算容量是否足够,
  7. // 若“当前实际容量 < 需要的容量”,则将容量x2。
  8. if (numKeysToBeAdded > threshold) {
  9. int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
  10. if (targetCapacity > MAXIMUM_CAPACITY)
  11. targetCapacity = MAXIMUM_CAPACITY;
  12. int newCapacity = table.length;
  13. while (newCapacity < targetCapacity)
  14. newCapacity <<= 1;
  15. if (newCapacity > table.length)
  16. resize(newCapacity);
  17. }
  18. // 通过迭代器,将“m”中的元素逐个添加到HashMap中。
  19. for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
  20. Map.Entry<? extends K, ? extends V> e = i.next();
  21. put(e.getKey(), e.getValue());
  22. }
  23. }

3.3.8 remove()

remove() 的作用是删除“键为key”元素

  1. public V remove(Object key) {
  2. Entry<K,V> e = removeEntryForKey(key);
  3. return (e == null ? null : e.value);
  4. }
  5. // 删除“键为key”的元素
  6. final Entry<K,V> removeEntryForKey(Object key) {
  7. // 获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算
  8. int hash = (key == null) ? 0 : hash(key.hashCode());
  9. int i = indexFor(hash, table.length);
  10. Entry<K,V> prev = table[i];
  11. Entry<K,V> e = prev;
  12. // 删除链表中“键为key”的元素
  13. // 本质是“删除单向链表中的节点”
  14. while (e != null) {
  15. Entry<K,V> next = e.next;
  16. Object k;
  17. if (e.hash == hash &&
  18. ((k = e.key) == key || (key != null && key.equals(k)))) {
  19. modCount++;
  20. size--;
  21. if (prev == e)
  22. table[i] = next;
  23. else
  24. prev.next = next;
  25. e.recordRemoval(this);
  26. return e;
  27. }
  28. prev = e;
  29. e = next;
  30. }
  31. return e;
  32. }

第3.4部分 HashMap实现的Cloneable接口

HashMap实现了Cloneable接口,即实现了clone()方法。
clone()方法的作用很简单,就是克隆一个HashMap对象并返回。

  1. // 克隆一个HashMap,并返回Object对象
  2. public Object clone() {
  3. HashMap<K,V> result = null;
  4. try {
  5. result = (HashMap<K,V>)super.clone();
  6. } catch (CloneNotSupportedException e) {
  7. // assert false;
  8. }
  9. result.table = new Entry[table.length];
  10. result.entrySet = null;
  11. result.modCount = 0;
  12. result.size = 0;
  13. result.init();
  14. // 调用putAllForCreate()将全部元素添加到HashMap中
  15. result.putAllForCreate(this);
  16. return result;
  17. }

第3.5部分 HashMap实现的Serializable接口

HashMap实现java.io.Serializable,分别实现了串行读取、写入功能。
串行写入函数是writeObject(),它的作用是将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中。
而串行读取函数是readObject(),它的作用是将HashMap的“总的容量,实际容量,所有的Entry”依次读出

  1. // java.io.Serializable的写入函数
  2. // 将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中
  3. private void writeObject(java.io.ObjectOutputStream s)
  4. throws IOException
  5. {
  6. Iterator<Map.Entry<K,V>> i =
  7. (size > 0) ? entrySet0().iterator() : null;
  8. // Write out the threshold, loadfactor, and any hidden stuff
  9. s.defaultWriteObject();
  10. // Write out number of buckets
  11. s.writeInt(table.length);
  12. // Write out size (number of Mappings)
  13. s.writeInt(size);
  14. // Write out keys and values (alternating)
  15. if (i != null) {
  16. while (i.hasNext()) {
  17. Map.Entry<K,V> e = i.next();
  18. s.writeObject(e.getKey());
  19. s.writeObject(e.getValue());
  20. }
  21. }
  22. }
  23. // java.io.Serializable的读取函数:根据写入方式读出
  24. // 将HashMap的“总的容量,实际容量,所有的Entry”依次读出
  25. private void readObject(java.io.ObjectInputStream s)
  26. throws IOException, ClassNotFoundException
  27. {
  28. // Read in the threshold, loadfactor, and any hidden stuff
  29. s.defaultReadObject();
  30. // Read in number of buckets and allocate the bucket array;
  31. int numBuckets = s.readInt();
  32. table = new Entry[numBuckets];
  33. init(); // Give subclass a chance to do its thing.
  34. // Read in size (number of Mappings)
  35. int size = s.readInt();
  36. // Read the keys and values, and put the mappings in the HashMap
  37. for (int i=0; i<size; i++) {
  38. K key = (K) s.readObject();
  39. V value = (V) s.readObject();
  40. putForCreate(key, value);
  41. }
  42. }

第4部分 HashMap遍历方式

4.1 遍历HashMap的键值对

第一步:根据entrySet()获取HashMap的“键值对”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

  1. // 假设map是HashMap对象
  2. // map中的key是String类型,value是Integer类型
  3. Integer integ = null;
  4. Iterator iter = map.entrySet().iterator();
  5. while(iter.hasNext()) {
  6. Map.Entry entry = (Map.Entry)iter.next();
  7. // 获取key
  8. key = (String)entry.getKey();
  9. // 获取value
  10. integ = (Integer)entry.getValue();
  11. }

4.2 遍历HashMap的键

第一步:根据keySet()获取HashMap的“键”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

  1. // 假设map是HashMap对象
  2. // map中的key是String类型,value是Integer类型
  3. String key = null;
  4. Integer integ = null;
  5. Iterator iter = map.keySet().iterator();
  6. while (iter.hasNext()) {
  7. // 获取key
  8. key = (String)iter.next();
  9. // 根据key,获取value
  10. integ = (Integer)map.get(key);
  11. }

4.3 遍历HashMap的值

第一步:根据value()获取HashMap的“值”的集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合

  1. // 假设map是HashMap对象
  2. // map中的key是String类型,value是Integer类型
  3. Integer value = null;
  4. Collection c = map.values();
  5. Iterator iter= c.iterator();
  6. while (iter.hasNext()) {
  7. value = (Integer)iter.next();
  8. }

遍历测试程序如下

  1. import java.util.Map;
  2. import java.util.Random;
  3. import java.util.Iterator;
  4. import java.util.HashMap;
  5. import java.util.HashSet;
  6. import java.util.Map.Entry;
  7. import java.util.Collection;
  8. /*
  9. * @desc 遍历HashMap的测试程序。
  10. * (01) 通过entrySet()去遍历key、value,参考实现函数:
  11. * iteratorHashMapByEntryset()
  12. * (02) 通过keySet()去遍历key、value,参考实现函数:
  13. * iteratorHashMapByKeyset()
  14. * (03) 通过values()去遍历value,参考实现函数:
  15. * iteratorHashMapJustValues()
  16. *
  17. * @author skywang
  18. */
  19. public class HashMapIteratorTest {
  20. public static void main(String[] args) {
  21. int val = 0;
  22. String key = null;
  23. Integer value = null;
  24. Random r = new Random();
  25. HashMap map = new HashMap();
  26. for (int i=0; i<12; i++) {
  27. // 随机获取一个[0,100)之间的数字
  28. val = r.nextInt(100);
  29. key = String.valueOf(val);
  30. value = r.nextInt(5);
  31. // 添加到HashMap中
  32. map.put(key, value);
  33. System.out.println(" key:"+key+" value:"+value);
  34. }
  35. // 通过entrySet()遍历HashMap的key-value
  36. iteratorHashMapByEntryset(map) ;
  37. // 通过keySet()遍历HashMap的key-value
  38. iteratorHashMapByKeyset(map) ;
  39. // 单单遍历HashMap的value
  40. iteratorHashMapJustValues(map);
  41. }
  42. /*
  43. * 通过entry set遍历HashMap
  44. * 效率高!
  45. */
  46. private static void iteratorHashMapByEntryset(HashMap map) {
  47. if (map == null)
  48. return ;
  49. System.out.println("\niterator HashMap By entryset");
  50. String key = null;
  51. Integer integ = null;
  52. Iterator iter = map.entrySet().iterator();
  53. while(iter.hasNext()) {
  54. Map.Entry entry = (Map.Entry)iter.next();
  55. key = (String)entry.getKey();
  56. integ = (Integer)entry.getValue();
  57. System.out.println(key+" -- "+integ.intValue());
  58. }
  59. }
  60. /*
  61. * 通过keyset来遍历HashMap
  62. * 效率低!
  63. */
  64. private static void iteratorHashMapByKeyset(HashMap map) {
  65. if (map == null)
  66. return ;
  67. System.out.println("\niterator HashMap By keyset");
  68. String key = null;
  69. Integer integ = null;
  70. Iterator iter = map.keySet().iterator();
  71. while (iter.hasNext()) {
  72. key = (String)iter.next();
  73. integ = (Integer)map.get(key);
  74. System.out.println(key+" -- "+integ.intValue());
  75. }
  76. }
  77. /*
  78. * 遍历HashMap的values
  79. */
  80. private static void iteratorHashMapJustValues(HashMap map) {
  81. if (map == null)
  82. return ;
  83. Collection c = map.values();
  84. Iterator iter= c.iterator();
  85. while (iter.hasNext()) {
  86. System.out.println(iter.next());
  87. }
  88. }
  89. }

第5部分 HashMap示例

下面通过一个实例学习如何使用HashMap

  1. import java.util.Map;
  2. import java.util.Random;
  3. import java.util.Iterator;
  4. import java.util.HashMap;
  5. import java.util.HashSet;
  6. import java.util.Map.Entry;
  7. import java.util.Collection;
  8. /*
  9. * @desc HashMap测试程序
  10. *
  11. * @author skywang
  12. */
  13. public class HashMapTest {
  14. public static void main(String[] args) {
  15. testHashMapAPIs();
  16. }
  17. private static void testHashMapAPIs() {
  18. // 初始化随机种子
  19. Random r = new Random();
  20. // 新建HashMap
  21. HashMap map = new HashMap();
  22. // 添加操作
  23. map.put("one", r.nextInt(10));
  24. map.put("two", r.nextInt(10));
  25. map.put("three", r.nextInt(10));
  26. // 打印出map
  27. System.out.println("map:"+map );
  28. // 通过Iterator遍历key-value
  29. Iterator iter = map.entrySet().iterator();
  30. while(iter.hasNext()) {
  31. Map.Entry entry = (Map.Entry)iter.next();
  32. System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());
  33. }
  34. // HashMap的键值对个数
  35. System.out.println("size:"+map.size());
  36. // containsKey(Object key) :是否包含键key
  37. System.out.println("contains key two : "+map.containsKey("two"));
  38. System.out.println("contains key five : "+map.containsKey("five"));
  39. // containsValue(Object value) :是否包含值value
  40. System.out.println("contains value 0 : "+map.containsValue(new Integer(0)));
  41. // remove(Object key) : 删除键key对应的键值对
  42. map.remove("three");
  43. System.out.println("map:"+map );
  44. // clear() : 清空HashMap
  45. map.clear();
  46. // isEmpty() : HashMap是否为空
  47. System.out.println((map.isEmpty()?"map is empty":"map is not empty") );
  48. }
  49. }

(某一次)运行结果: 

  1. map:{two=7, one=9, three=6}
  2. next : two - 7
  3. next : one - 9
  4. next : three - 6
  5. size:3
  6. contains key two : true
  7. contains key five : false
  8. contains value 0 : false
  9. map:{two=7, one=9}
  10. map is empty

 

本文转载自http://www.cnblogs.com/skywang12345/p/3310835.html致敬原作者

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/971019
推荐阅读
相关标签
  

闽ICP备14008679号