HashMap是最常用的集合类框架之一,它实现了Map接口,所以存储的元素也是键值对映射的结构,并允许使用null值和null键,其内元素是无序的,如果要保证有序,可以使用LinkedHashMap。HashMap是线程不安全的,下篇文章会讨论。HashMap的类关系如下:

    java.util 

    Class HashMap<K,V>

      java.lang.Object

        |--java.util.AbstractMap<K,V>

            |--java.util.HashMap<K,V>

所有已实现的接口:

  Serializable,Cloneable,Map<K,V>

直接已知子类:

  LinkedHashMap,PrinterStateReasons

  HashMap中用的最多的方法就属put() 和 get() 方法;HashMap的Key值是唯一的,那如何保证唯一性呢?我们首先想到的是用equals比较,没错,这样可以实现,但随着内部元素的增多,put和get的效率将越来越低,这里的时间复杂度是O(n),假如有1000个元素,put时最差情况需要比较1000次。实际上,HashMap很少会用到equals方法,因为其内通过一个哈希表管理所有元素,哈希是通过hash单词音译过来的,也可以称为散列表,哈希算法可以快速的存取元素,当我们调用put存值时,HashMap首先会调用Key的hash方法,计算出哈希码,通过哈希码快速找到某个存放位置(桶),这个位置可以被称之为bucketIndex,但可能会存在多个元素找到了相同的bucketIndex,有个专业名词叫碰撞,当碰撞发生时,这时会取到bucketIndex位置已存储的元素,最终通过equals来比较,equals方法就是碰撞时才会执行的方法,所以前面说HashMap很少会用到equals。HashMap通过hashCode和equals最终判断出Key是否已存在,如果已存在,则使用新Value值替换旧Value值,并返回旧Value值,如果不存在 ,则存放新的键值对<K, V>到bucketIndex位置。通过下面的流程图来梳理一下整个put过程。

最终HashMap的存储结构会有这三种情况,我们当然期望情形3是最少发生的(效率最低)。

HashMap 碰撞问题处理:

  碰撞:所谓“碰撞”就上面所述是多个元素计算得出相同的hashCode,在put时出现冲突。

  处理方法:

  Java中HashMap是利用“拉链法”处理HashCode的碰撞问题。在调用HashMap的put方法或get方法时,都会首先调用hashcode方法,去查找相关的key,当有冲突时,再调用equals方法。hashMap基于hasing原理,我们通过put和get方法存取对象。当我们将键值对传递给put方法时,他调用键对象的hashCode()方法来计算hashCode,然后找到bucket(哈希桶)位置来存储对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。hashMap在每个链表节点存储键值对对象。当两个不同的键却有相同的hashCode时,他们会存储在同一个bucket位置的链表中。键对象的equals()来找到键值对。

HashMap基本结构概念图:

到目前为止,我们了解了两件事:

  1、HashMap通过键的hashCode来快速的存取元素。

  2、当不同的对象发生碰撞时,HashMap通过单链表来解决,将新元素加入链表表头,通过next指向原有的元素。单链表在Java中的实现就是对象的引用(复合)。

HashMap.put()和get()源码:

/** * Returns the value to which the specified key is mapped, * or if this map contains no mapping for the key. * * 获取key对应的value */
public V get(Object key) {  if (key == null)  return getForNullKey();  //获取key的hash值  int hash = hash(key.hashCode());  // 在“该hash值对应的链表”上查找“键值等于key”的元素  for (Entry<K,V> e = table[indexFor(hash, table.length)];  e != null;  e = e.next) {  Object k;  if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  return e.value;  }  return null;
}  /** * Offloaded version of get() to look up null keys.  Null keys map * to index 0.   * 获取key为null的键值对,HashMap将此键值对存储到table[0]的位置 */
private V getForNullKey() {  for (Entry<K,V> e = table[0]; e != null; e = e.next) {  if (e.key == null)  return e.value;  }  return null;
}  /** * Returns <tt>true</tt> if this map contains a mapping for the * specified key. * * HashMap是否包含key */
public boolean containsKey(Object key) {  return getEntry(key) != null;
}  /** * Returns the entry associated with the specified key in the * HashMap.   * 返回键为key的键值对 */
final Entry<K,V> getEntry(Object key) {  //先获取哈希值。如果key为null,hash = 0;这是因为key为null的键值对存储在table[0]的位置。  int hash = (key == null) ? 0 : hash(key.hashCode());  //在该哈希值对应的链表上查找键值与key相等的元素。  for (Entry<K,V> e = table[indexFor(hash, table.length)];  e != null;  e = e.next) {  Object k;  if (e.hash == hash &&  ((k = e.key) == key || (key != null && key.equals(k))))  return e;  }  return null;
}  /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * 将“key-value”添加到HashMap中,如果hashMap中包含了key,那么原来的值将会被新值取代 */
public V put(K key, V value) {  //如果key是null,那么调用putForNullKey(),将该键值对添加到table[0]中  if (key == null)  return putForNullKey(value);  //如果key不为null,则计算key的哈希值,然后将其添加到哈希值对应的链表中  int hash = hash(key.hashCode());  int i = indexFor(hash, table.length);  for (Entry<K,V> e = table[i]; e != null; e = e.next) {  Object k;  //如果这个key对应的键值对已经存在,就用新的value代替老的value。  if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  V oldValue = e.value;  e.value = value;  e.recordAccess(this);  return oldValue;  }  }  modCount++;  addEntry(hash, key, value, i);  return null;
}

从HashMap的put()和get方法实现中可以与拉链法解决hashCode冲突解决方法相互印证。并且从put方法中可以看出HashMap是使用Entry<K,V>来存储数据。

HashMap碰撞问题解析相关推荐

  1. hashmap删除指定key_Java集合之HashMap源码解析(JDK8)

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景非常丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  2. 通俗易懂Hashmap源码解析

    Hashmap源码解析 一.Hashmap数据结构 哈希表是一种以键 - 值(key-value)存储数据的结构,我们只要输入待查找的值即 key,就可以找到其对应的值即 Value.哈希的思路很简单 ...

  3. 红黑树分析与JDK8中HashMap源码解析

    红黑树分析与JDK8中HashMap源码解析 BST O(1), O(n), O(logn), O(nlogn) 的区别 红黑树-RBTree 插入数据 HashMap中红黑树的插入操作 HashMa ...

  4. HashMap源码解析(JDK1.8)

    HashMap源码解析(JDK1.8) 目录 定义 构造函数 数据结构 存储实现源码分析 删除操作源码分析 hashMap遍历和异常解析 1. 定义 HashMap实现了Map接口,继承Abstrac ...

  5. 面试官系统精讲Java源码及大厂真题 - 08 HashMap 源码解析

    08 HashMap 源码解析 自信和希望是青年的特权. --大仲马 引导语 HashMap 源码很长,面试的问题也非常多,但这些面试问题,基本都是从源码中衍生出来的,所以我们只需要弄清楚其底层实现原 ...

  6. Java集合框架之三:HashMap源码解析

    Java集合框架之三:HashMap源码解析 版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! HashMap在我们的工作中应用的非常广泛,在工作面试中也经常会被问到,对于这样一个重要的集 ...

  7. 史上最详细的HashMap红黑树解析

    HashMap红黑树解析 红黑树介绍 TreeNode结构 树化的过程 红黑树的左旋和右旋 TreeNode的左旋和右旋 红黑树的插入 TreeNode的插入 红黑树的删除 TreeNode的删除节点 ...

  8. java容器三:HashMap源码解析

    前言:Map接口 map是一个存储键值对的集合,实现了Map接口的主要类有以下几种 TreeMap:用红黑树实现 HashMap:数组和链表实现 HashTable:与HashMap类似,但是线程安全 ...

  9. HashMap源码解析——基于JDK1.8

    前言 HashMap数据结构由数组和链表(超过一定数量转换为红黑树)组成,在进行增删查等操作时,首先要定位到元素的所在表的位置,之后再从链表中定位该元素.具体找到表下标的方法是(n - 1) & ...

  10. JDK8:HashMap源码解析:get方法、containsKey方法、getNode方法

    一.概述 HashMap存储的键值对,用put(K,V)方法来存储,用get(K)方法来获取V,用containsKey(K)方法来检查K是否存在.可先参见:put方法解析 来了解键值对的存储原理,再 ...

最新文章

  1. 使用 Python 的基于边缘和基于区域的分割
  2. 初学PHP:用post传递checkbox
  3. 网络加速和优化控制常用管理
  4. 斐波那契数 c 语言实现
  5. phpstrom 里面的 文件修改后 的*怎样设置
  6. ubuntu下面的robo3t 使用笔记
  7. Linux中重要文件
  8. 前端学习(1439):vue的helloworld
  9. python queue查询空_【Python】多线程爬虫案例
  10. FreeEIM通讯软件平均需要5分钟才能
  11. java unsigined short_LDD之数据类型
  12. Linux查找历史命令
  13. Java基础学习总结(137)——Java ClassLoader再总结
  14. pickle.dump()把大量数据写入文件发生MemoryError解决方法
  15. SecureCRT在linux与windows本地互传文件之SFTP
  16. setinterval 和 ajax,JavaScriptsetInterval和“this”解决方案
  17. 非好友怎么进qq权限空间
  18. 操作系统 | Linux基础教程
  19. Linux radius客户端,FreeRadius客户端基础配置及测试
  20. 1.54寸墨水屏驱动板原理图+PCB

热门文章

  1. VoIP技术的基本原理与应用
  2. 详解百度快照劫持,小白必看篇
  3. 暑期计算机数学培训心得体会,关于暑期培训心得体会三篇
  4. PCI驱动开发学习笔记(一)
  5. (CVPR-2021)动态区域感知卷积
  6. mysql瀑布流布局插件_瀑布流JS插件
  7. 详解物理学四大神兽————麦克斯韦妖
  8. 浅谈二十年后电气工程前途
  9. LOL喷子专用自动骂人工具,2018更新完整版!
  10. 团队想招人,如何说服公司?写一封这样的人员招聘申请邮件,再看效果!