在java中HashMap作为一种Map的实现,在程序中我们经常会用到,在此记录下其中get与put的执行过程,以及其hash冲突的解决方式:

  HashMap在存储数据的时候是key-value的键值对的形式存放的,一个key-value会创建一个Map.Entry实现类,在HashMap中该实现类分为Node和TreeNode,其中TreeNode继承了Node类,在没有hash冲突的情况下,这些Map.Entry实现类会组成数组table存放。key的hashCode值决定了该Map.Entry在table中的索引

  

  HashMap的hash算法是key的hashCode异或key的hashCode无符号右移16位

  

  在放入key到一定程度的时候就会出现不同的key值运算出相同的hash值,导致多个Map.Entry类存放在table同一个索引位置上,这个时候就是所谓的hash冲突。

通过观察put(key,value)方法:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

  我们发现,在执行过程中会首先取出table在(table.length-1)&hash(key)的值,如果该值为空,说明还没有发生hash冲突,直接new一个Node放在该索引处,

如果该值不为空,说明已经存放过相同hash值的key,继续判断两个key的equals方法是否相等,如果相等则覆盖原来的值。如果不等,则出现hash冲突。

  在出现hash冲突时首先会判断当前的Node是否是TreeNode,如果是TreeNode则在该TreeNode上添加一个分支。如果不是,那么说明是Node类。在table中只会存在这两个Map.Entry实现类。在所有的Node都有一个next变量,当出现hash冲突时,就会将该Node的next变量赋值为要放入的新的Node,这样在多次冲突后该位置就会形成一个类似于链表的结构,当该链表长度为8时,为了提高性能,就会将该链表替换成树结构的TreeNode。

  以上就是HashMap解决hash冲突的方式。

  当调用get方法时

 public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}

  会通过key的hash值获取table中的Node,并通过key的equals方法来确定要取的Node,在返回Node的value值。

  通过HashMap的存储结构可以发现当我们遍历HashMap时通过entrySet方法性能会高一点,因为它直接返回了存储的Map.Entry类,而遍历key方式是通过遍历Map.Entry取出key,我们在调用get(key)方法时又会去取一次Entry,所以性能会比较低

Map之HashMap的get与put流程,及hash冲突解决方式的更多相关文章

  1. Java中常见数据结构Map之HashMap

    之前很早就在博客中写过HashMap的一些东西: 彻底搞懂HashMap,HashTableConcurrentHashMap关联: http://www.cnblogs.com/wang-meng/ ...

  2. Java 从 Map 到 HashMap 的一步步实现

    Java 从 Map 到 HashMap 的一步步实现 一. Map 1.1 Map 接口 在 Java 中, Map 提供了键--值的映射关系.映射不能包含重复的键,并且每个键只能映射到一个值. 以 ...

  3. Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)

    概要 学完了Map的全部内容,我们再回头开开Map的框架图. 本章内容包括:第1部分 Map概括第2部分 HashMap和Hashtable异同第3部分 HashMap和WeakHashMap异同 转 ...

  4. JAVA基础学习day16--集合三-Map、HashMap,TreeMap与常用API

    一.Map简述 1.1.简述 public interface Map<K,V> 类型参数: K - 此映射所维护的键的类型 key V - 映射值的类型 value 该集合提供键--值的 ...

  5. 【转】java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别

    原文网址:http://www.360doc.com/content/15/0427/22/1709014_466468021.shtml java 容器类使用 Collection,Map,Hash ...

  6. Map map=new HashMap(); 为什么是这样

    Map是接口,hashMap是Map的一种实现.接口不能被实例化. Map map=new HashMap(); 就是将map实例化成一个hashMap.这样做的好处是调用者不需要知道map具体的实现 ...

  7. (10)集合之双列集合Map,HashMap,TreeMap

    Map中的元素是两个对象,一个对象作为键,一个对象作为值.键不可以重复,但是值可以重复. 看顶层共性方法找子类特有对象. Map与Collection在集合框架中属并列存在 Map存储的是键值对 Ma ...

  8. java集合系列——Map之HashMap介绍(八)

    1.HashMap的简介 (JDK1.7.0_79版本) HashMap是基于哈希表的Map实现的的,一个Key对应一个Value,允许使用null键和null值,不保证映射的顺序,特别是它不保证该顺 ...

  9. java容器类2:Map及HashMap深入解读

    Java的编程过程中经常会和Map打交道,现在我们来一起了解一下Map的底层实现,其中的思想结构对我们平时接口设计和编程也有一定借鉴作用.(以下接口分析都是以jdk1.8源码为参考依据) 1. Map ...

随机推荐

  1. python网络编程系列

    计算机基础 网络基础 套接字 socket模块 TCP协议和UDP协议 struct 模块简介 struct 模块解决 TCP黏包问题 socket 客户端的认证 socketserver模块初识 客 ...

  2. springcloud 中文文档

    spring cloud 中文文档:https://springcloud.cc/spring-cloud-dalston.html spring cloud 中文网:https://springcl ...

  3. 用户命令行方式连MYSQL数据库

    现在是手工入门,就不太依赖IDE,使用MYSQL的JDBC的JAR包连数据库的方式如下: 演示文件内容: package cc.openhome; import java.sql.*; public ...

  4. HDU 3389

    对于这道题,我们需要从(A+B)%3==0这式子考虑.对于第一条式子,我们可以知道,只能是奇偶盒子交替转移. 由第二条式子可知,要么是同余为0的A,B之间转移,要么是余数为1,2之间的 转移.后来仔细 ...

  5. 怎样从C++代码直接訪问android framework层的WifiService

    说究竟,Java层的service就是就C++层的binder的封装.所以从原理上来讲通过C++代码直接訪问android framework层的service是全然可能的,这篇文章以訪问WifiSe ...

  6. PL/SQL Developer自己主动补全SQL语句

    快捷输入SQL语句,比如输入s,按下空格,自己主动替换成SELECT;再比如,输入se,按下空格, 自己主动替换成SELECT * FROM,很方便,节省了大量的时间去编写反复的SQL语句. 设置方法 ...

  7. luogu1969 积木大赛

    题目大意 搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成n块高度为 0 的积木).接下来每次操作,可以选择一段连 ...

  8. android的低内存管理器【转】

    本文转载自:http://blog.csdn.net/haitaoliang/article/details/22092321 版权声明:本文为博主原创文章,未经博主允许不得转载. 安卓应用不用太在意 ...

  9. express 4.2.0 使用session和cookies

    express 4.2.0 使用session和cookies https://my.oschina.net/u/1466553/blog/294336

  10. POJ3087 Shuffle'm Up

    题目: 现有字符串s1.s2.s12,其中s1.s2的长度为len,s12的长度为2*len. 是否可以通过一些操作使s1和s2转换合并成s12? 变换的操作规则如下: 假设s1=11111,s2=0 ...