千家信息网

JDK的HashMap怎么使用

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容主要讲解"JDK的HashMap怎么使用",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"JDK的HashMap怎么使用"吧!首先先来分析一下Hash
千家信息网最后更新 2026年11月03日JDK的HashMap怎么使用

本篇内容主要讲解"JDK的HashMap怎么使用",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"JDK的HashMap怎么使用"吧!

首先先来分析一下HashMap

static final int DEFAULT_INITIAL_CAPACITY = 16;

最大容量2的15次方+1;
static final int MAXIMUM_CAPACITY = 1 << 30;

默认加载因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;

内部实现的机制是用具有键值对格式的单个的entry数组实现:

  1. transient Entry[] table;


  2. static class Entry implements Map.Entry {

  3. final K key; //键

  4. V value; // 值

  5. Entry next; //下一个

  6. final int hash; //哈西值


  7. /**

  8. * Creates new entry.

  9. */

  10. Entry(int h, K k, V v, Entry n) {

  11. value = v;

  12. next = n;

  13. key = k;

  14. hash = h;

  15. }


  16. public final K getKey() {

  17. return key;

  18. }


  19. public final V getValue() {

  20. return value;

  21. }


  22. public final V setValue(V newValue) {

  23. V oldValue = value;

  24. value = newValue;

  25. return oldValue;

  26. }


  27. public final boolean equals(Object o) {

  28. if (!(o instanceof Map.Entry))

  29. return false;

  30. Map.Entry e = (Map.Entry)o;

  31. Object k1 = getKey();

  32. Object k2 = e.getKey();

  33. if (k1 == k2 || (k1 != null && k1.equals(k2))) {

  34. Object v1 = getValue();

  35. Object v2 = e.getValue();

  36. if (v1 == v2 || (v1 != null && v1.equals(v2)))

  37. return true;

  38. }

  39. return false;

  40. }


  41. public final int hashCode() {

  42. return (key==null ? 0 : key.hashCode()) ^

  43. (value==null ? 0 : value.hashCode());

  44. }


  45. public final String toString() {

  46. return getKey() + "=" + getValue();

  47. }



  48. void recordAccess(HashMap m) {

  49. }


  50. void recordRemoval(HashMap m) {

  51. }

  52. }

当前的数组大小:
transient int size;

构造函数初始化:
设置初始化的容积和加载因子

  1. public HashMap(int initialCapacity, float loadFactor) {


  2. //初始化容积必须大于0

  3. if (initialCapacity < 0)

  4. throw new IllegalArgumentException("Illegal initial capacity: " +

  5. initialCapacity);

  6. //超过最大容积的时候,那么等于最大容积

  7. if (initialCapacity > MAXIMUM_CAPACITY)

  8. initialCapacity = MAXIMUM_CAPACITY;


  9. //初始化加载因子;

  10. if (loadFactor <= 0 || Float.isNaN(loadFactor))

  11. throw new IllegalArgumentException("Illegal load factor: " +

  12. loadFactor);


  13. //找出一个数的平方大于当前给出的初始化容积,比如是初始化是10的话,那么最终的容积就是16

  14. int capacity = 1;

  15. while (capacity < initialCapacity)

  16. capacity <<= 1;


  17. this.loadFactor = loadFactor;

  18. threshold = (int)(capacity * loadFactor);


  19. //用容积来初始化数组大小;size当前还是为0;

  20. table = new Entry[capacity];


  21. //初始化动作,可以留给子类实现,模板模式的应用

  22. init();

  23. }


这是一个hashcode的转换算法;他能够把对象的hashcode转换成为小于length-1的整数作为数组的下标;那么势必会出现重合

  1. static int hash(int h) {

  2. h ^= (h >>> 20) ^ (h >>> 12);

  3. return h ^ (h >>> 7) ^ (h >>> 4);

  4. }


  5. static int indexFor(int h, int length) {

  6. return h & (length-1);

  7. }



我们先看增加方法:

  1. public V put(K key, V value) {

  2. //判断键值是否为空;

  3. if (key == null)

  4. return putForNullKey(value);


  5. //得到hashcode

  6. int hash = hash(key.hashCode());

  7. //得到一个小于数组长度(取的是与操作)的下标

  8. int i = indexFor(hash, table.length);

  9. //在数组中找到该下标的entry值;

  10. //事实上entry也是一个链表,相对于LinkedList来说,他的entry是单向的

  11. for (Entry e = table[i]; e != null; e = e.next) {

  12. Object k;

  13. //如果存在键值是同一对象的entry,那么用新值覆盖旧值,不存在则再往下找,知道末尾

  14. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

  15. V oldValue = e.value;

  16. e.value = value;

  17. e.recordAccess(this);

  18. return oldValue;

  19. }

  20. }

  21. //增加修改次数

  22. modCount++;

  23. //增加这个entry到该下标列表的首部

  24. addEntry(hash, key, value, i);

  25. return null;

  26. }


  27. void addEntry(int hash, K key, V value, int bucketIndex) {

  28. Entry e = table[bucketIndex];

  29. //可见是放在該下标链表的第一位的

  30. table[bucketIndex] = new Entry(hash, key, value, e);

  31. //在这里增加了size;

  32. if (size++ >= threshold)

  33. resize(2 * table.length);

  34. }

如果key是null的话:
那么他可以不用通过hashcode定位数组中的队列下标-_-||事实上他也没有hashcode;规定在0位存放这个链表的头;可见在HashMap中是可以

存放null的key的;但是正因为其没有hashcode那么就只能存放一个元素,而不是像其他一样能存放多个;但是另外我们可见,你可以考虑把使

用的最多的值的键设置成为null,因为他是找到的最快的;


  1. private V putForNullKey(V value) {

  2. for (Entry e = table[0]; e != null; e = e.next) {

  3. if (e.key == null) {

  4. V oldValue = e.value;

  5. e.value = value;

  6. e.recordAccess(this);

  7. return oldValue;

  8. }

  9. }

  10. modCount++;

  11. addEntry(0, null, value, 0);

  12. return null;

  13. }


上面说了存放了,下面我们再来看看如何取出来把:

  1. final Entry getEntry(Object key) {

  2. //经过算法得到这个key对应的hashcode,可见hashcode是固定的对应,而不是随机的;如果是null的话为0,经过与操作还是0,直接


  3. //定位到table[0]否则查找出下标

  4. int hash = (key == null) ? 0 : hash(key.hashCode());

  5. for (Entry e = table[indexFor(hash, table.length)];

  6. e != null;

  7. e = e.next) {

  8. Object k;

  9. //匹配这个key的hashcode和数值,可见不是同一的值其hashcode是可能一样的,否则如果是一一对应则没必要匹配key了

  10. if (e.hash == hash &&

  11. ((k = e.key) == key || (key != null && key.equals(k))))

  12. return e;

  13. }

  14. //没有找到,为空;

  15. return null;

我们再来看看其容量是如何增长的:

  1. public void putAll(Map m) {

  2. //得到新增加进来的元素的个数

  3. int numKeysToBeAdded = m.size();

  4. if (numKeysToBeAdded == 0)

  5. return;

  6. //如果新增加的元素个数大于原来容积*加载因子;可见我们必须要扩充容量了

  7. if (numKeysToBeAdded > threshold) {

  8. //那么我们增加的容量将是该新增元素个数+预留空间的总数

  9. int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);

  10. if (targetCapacity > MAXIMUM_CAPACITY)

  11. targetCapacity = MAXIMUM_CAPACITY;

  12. int newCapacity = table.length;

  13. //如果我们现在的表的容量小于新增加的容量,那么我们扩展两倍,如果还小,再扩大两倍,直到他的大于当前需要的容量

  14. while (newCapacity < targetCapacity)

  15. newCapacity <<= 1;

  16. if (newCapacity > table.length)

  17. resize(newCapacity);

  18. }


  19. //添置新的"家具"

  20. for (Iterator> i = m.entrySet().iterator(); i.hasNext(); ) {

  21. Map.Entry e = i.next();

  22. put(e.getKey(), e.getValue());

  23. }

  24. }

用新的容积开辟了一块足够大的存储空间,

  1. void resize(int newCapacity) {

  2. Entry[] oldTable = table;

  3. int oldCapacity = oldTable.length;

  4. if (oldCapacity == MAXIMUM_CAPACITY) {

  5. threshold = Integer.MAX_VALUE;

  6. return;

  7. }


  8. Entry[] newTable = new Entry[newCapacity];

  9. transfer(newTable);

  10. table = newTable;

  11. threshold = (int)(newCapacity * loadFactor);

  12. }

  13. 并且把家具从以前的"小房子"帮到了"大房子"

  14. void transfer(Entry[] newTable) {

  15. Entry[] src = table;

  16. int newCapacity = newTable.length;

  17. for (int j = 0; j < src.length; j++) {

  18. //得到各个数组元素的链表的头

  19. Entry e = src[j];

  20. if (e != null) {

  21. src[j] = null; //一定要把原来的"小房子"收拾好,方便垃圾回收;

  22. do {

  23. Entry next = e.next;

  24. int i = indexFor(e.hash, newCapacity);

  25. e.next = newTable[i];

  26. newTable[i] = e;

  27. e = next;

  28. } while (e != null);

  29. }

  30. }

  31. }

新增,查找,都看过了,我们来看一下删除:

  1. public V remove(Object key) {

  2. Entry e = removeEntryForKey(key);

  3. return (e == null ? null : e.value);

  4. }



  5. final Entry removeEntryForKey(Object key) {

  6. //定位查找到链表表头;

  7. int hash = (key == null) ? 0 : hash(key.hashCode());

  8. int i = indexFor(hash, table.length);

  9. Entry prev = table[i];

  10. Entry e = prev;


  11. while (e != null) {

  12. Entry next = e.next;

  13. Object k;

  14. if (e.hash == hash &&

  15. ((k = e.key) == key || (key != null && key.equals(k)))) {

  16. modCount++;

  17. //若能找到,修改前后的链表节点的指向,从中删除;

  18. size--;

  19. if (prev == e)

  20. table[i] = next;

  21. else

  22. prev.next = next;

  23. e.recordRemoval(this);

  24. return e;

  25. }

  26. //方便链表往下传递;保存prev是因为是单向链表,所以一旦找到的目标节点,无法通过该节点得到钱一个节点,就无法更改前


  27. 一个节点的指//向,所以要保存prev

  28. prev = e;

  29. e = next;

  30. }


  31. return e;

  32. }


现在我们再看一下如何单独取到他的键值集合或者值集合:

  1. values实现了一个内部私有类;

  2. public Collection values() {

  3. Collection vs = values;

  4. return (vs != null ? vs : (values = new Values()));

  5. }


  6. //AbstractCollection 把一些基本的curd委托给了iteartor,所以只需重载其获取iteartor的方法;

  7. private final class Values extends AbstractCollection {


  8. //重载了iteartor

  9. public Iterator iterator() {

  10. return newValueIterator();

  11. }

  12. public int size() {

  13. return size;

  14. }

  15. public boolean contains(Object o) {

  16. return containsValue(o);

  17. }

  18. public void clear() {

  19. HashMap.this.clear();

  20. }

  21. }

  22. 下面是该iteartor的获取:其中主要的是看看我们获取的value的collection是如何排序的;


  23. private abstract class HashIterator implements Iterator {

  24. Entry next; // next entry to return

  25. int expectedModCount; // For fast-fail

  26. int index; // current slot

  27. Entry current; // current entry


  28. HashIterator() {

  29. expectedModCount = modCount;

  30. //如果该HashMap有元素

  31. if (size > 0) { // advance to first entry

  32. Entry[] t = table;

  33. 然后把next和index都调至该table数组的链表头中不为空的地方;

  34. while (index < t.length && (next = t[index++]) == null)

  35. ;

  36. }

  37. }


  38. public final boolean hasNext() {

  39. return next != null;

  40. }


  41. final Entry nextEntry() {

  42. if (modCount != expectedModCount)

  43. throw new ConcurrentModificationException();

  44. Entry e = next;

  45. if (e == null)

  46. throw new NoSuchElementException();

  47. //代表这个链表遍历完成了,需要开始遍历下一个table下标的链表了

  48. if ((next = e.next) == null) {

  49. Entry[] t = table;

  50. //找到下一个不为空的table元素

  51. while (index < t.length && (next = t[index++]) == null)

  52. ;

  53. }

  54. current = e;

  55. return e;

  56. }


  57. public void remove() {

  58. if (current == null)

  59. throw new IllegalStateException();

  60. if (modCount != expectedModCount)

  61. throw new ConcurrentModificationException();

  62. Object k = current.key;

  63. current = null;

  64. HashMap.this.removeEntryForKey(k);

  65. expectedModCount = modCount;

  66. }


  67. }



  68. ValueIterator :


  69. private final class ValueIterator extends HashIterator {

  70. public V next() {

  71. return nextEntry().value;

  72. }

  73. }

这是我们会问加入我在这个value的collection中增加元素会出现什么样的情况呢,次序会不会打乱,对不起,AbstractCollection不存在add

的,iteartor也是不允许增加元素的;

对于来说keySet来说是同理的,不过因为key是不存在一样的,所以,我们不会返回collection而返回set,避免了重复key的出现

到此,相信大家对"JDK的HashMap怎么使用"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

元素 容积 数组 下标 容量 节点 个数 因子 最大 方法 定位 事实 事实上 内容 单向 大小 家具 对象 房子 空间 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 万易互联网科技有限公司 长亮科技互联网金融 美国互联网前沿科技 科技伦理 互联网公司 隆华科技工业互联网 全球顶尖互联网科技公司 成都铂石互联网科技做什么的 上海宁丞科技互联网 晓花互联网科技有限公司网址 崇明区互联网教育科技售后服务 互联网科技研发水平 国大互联网教育科技有限公司工作 民营互联网公司国防科技 佛山市翊博互联网络科技有限公司 佛山市拉法互联网科技有限公司 滨湖区互联网智能科技产品有哪些 互联网时代的科技公司 北京市互联网新科技 浙南科技城互联网大厦招聘 上海互联网科技公司ppt 科技创新与互联网经济 广西互联网科技公司 互联网科技素材视频竖屏 互联网科技与管理 互联网科技扶贫时间 互联网方面的科技前沿 长沙渺峰互联网科技有限公司 10位科技互联网大佬 互联网领先科技成果宣传视频 网互联网络科技
0