HashMap采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。

解决哈希冲突的三个方法:

a.开放定址法

又被称为再散列法,包括线性探测再散列、二次探测再散列、伪随机探测再散列

b.再哈希法

地址冲突后,对哈希结果再次进行哈希,直到不冲突为止

c.链地址法

冲突后的元素组成一个链指向当前地址(HashMap采用的该方式,只是当链表长度超过8后,就会把链表改为红黑树)

以下是具体的put过程(JDK1.8版)

1、对Key求Hash值,然后再计算下标

2、如果没有碰撞,直接放入数组中(碰撞的意思是计算得到的Hash值相同,需要放到同一个bucket中)

3、如果碰撞了,以链表的方式链接到后面

4、如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表

5、如果节点已经存在就替换旧值

6、如果数组满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)

选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。而红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,引入红黑树就是为了查找数据快,解决链表查询深度的问题,我们知道红黑树属于平衡二叉树,但是为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少,所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。

下面从初始化一个HashMap及put一个键值对,来看下HashMap的resize()

Map exampleMap = new HashMap();

初始化Map则,可以看到exampleMap中,loadFactor是默认值0.75(loadFactor用于设置阈值,即到什么程度执行resize操作)

exampleMap.put("1",1)

map中放入数据,看下源代码做了什么

public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
} /**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {//hash是1的hash值,key是字符串1,value是1
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)//tab的length是16,n的值是16
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)//按一定的规则,确定新增的参数放在tab数组的tab[i]位置,如果该位置为空,则直接初始化Node放入该位置;
tab[i] = newNode(hash, key, value, null);
else {//如果tab[i]不为空进入该分支
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))//如果hash相同,key相同,则替换tab[i]
e = p;
else if (p instanceof TreeNode)//判断如果tab[i]是treeNode,则把当前数据放入红黑树中
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//如果tab[i]是普通Node,则在tab[i]维护的链尾部新增,当链接的Node数量大于8后,要把链改为红黑树结构存储
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)//添加Node之后判断当前map中数据量是否大于12(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR),如果大于需要resize,否则结束当前操作
resize();
afterNodeInsertion(evict);
return null;
}

java容器之HashMap的更多相关文章

  1. Java容器之HashMap源码分析

    在java的容器框架中,hashMap是最常用的容器之一,下面我们就来深入了解下它的数据结构和实现原理 先看下HashMap的继承结构图 下面针对各个实现类的特点进行下说明:1)HashMap: 它是 ...

  2. Java 容器之Hashset 详解

    Java 容器之Hashset 详解.http://blog.csdn.net/nvd11/article/details/27716511

  3. Java容器之旅:容器基础知识总结

    下图展示了Java容器类库的完备图,包括抽象类和遗留构件(不包括Queue的实现). 常用的容器用黑色粗线框表示,点线框表示接口,虚线框表示抽象类,实线框表示类,空心箭头表示实现关系.Produce表 ...

  4. Java 容器之 Connection栈队列及一些常用

    集合家族图 ---|Collection: 单列集合 ---|List: 有存储顺序 , 可重复 ---|ArrayList: 数组实现 , 查找快 , 增删慢 ---|LinkedList: 链表实 ...

  5. Java容器之Map接口

    Map 接口: 1. 实现 Map 接口的类是用来存储键-值(key-value)对: 2. Map 接口的实现类有 HashMap 和 TreeMap 等: 3. Map 类中存储的键-值对,通过键 ...

  6. Java容器之Collections

    Collections 类来源于 java.util.Collections,从 java.lang.object继承. 此类完全由在 collection 上进行操作或返回 collection 的 ...

  7. Java容器之List接口

    List 接口: 1. List 接口是 Collection 的子接口,实现 List 接口的容器类中的元素是有顺序的,而且可以重复: 2. List 容器中的元素都对应一个整数型的序号记载其在容器 ...

  8. Java容器之Iterator接口

    Iterator 接口: 1. 所有实现了Collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator接口的对象. 2. Iterator 对象称作迭代器,用以方便的 ...

  9. Java容器之Set接口

    Set 接口: 1. Set 接口是 Collection 的子接口,Set 接口没有提供额外的方法,但实现 Set 接口的容器类中的元素是没有顺序的,且不可以重复: 2. Set 容器可以与数学中的 ...

随机推荐

  1. goto语法在PHP中的使用

    在C++.Java及很多语言中,都存在着一个神奇的语法,就是goto.顾名思义,它的使用是直接去到某个地方.从来代码的角度来说,也就是直接跳转到指定的地方.我们的PHP中也有这个功能,我们先来看看它是 ...

  2. 了解PHP-FPM

    在服务器上,当我们查看php进程时,全都是php-fpm进程,大家都知道这个就是php的运行环境,那么,它到底是个什么东西呢? PHP-FPM简介 PHP-FPM,就是PHP的FastCGI管理器,用 ...

  3. Nginx系列(6)- nginx: [error] CreateFile() "D:\nginx-1.20.1/logs/nginx.pid" failed (2: The system cannot find the file specified)

    背景 修改nginx配置文件nginx.conf后,想要重启nginx使配置生效.cmd进入nginx安装目录,输入命令: nginx -s reload 报错:nginx: [error] Crea ...

  4. yapi 事件创建、修改等接口事件监听

    使用的yapi作为接口文档平台.出于业务需求需要对接口创建.修改.删除等事件进行监听. yapi已经实现并预留了这个口子,但是没有找到实现的文档.这里进行简单描述下使用的方式. 一.yapi创建.修改 ...

  5. greedy algorithm, insertion sort, quick sort

    always makes the choice that seems to be the best at that moment. Example #1: @function:  scheduling ...

  6. php-抽象工厂

    目标:创建有依赖关系的实例;(套餐) <?php //抽象类 食物 interface IAllayFood { function Allay(); } interface IDrinkFood ...

  7. 鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基础 | 百篇博客分析OpenHarmony源码 | v68.01

    子曰:"质胜文则野,文胜质则史.文质彬彬,然后君子." <论语>:雍也篇 百篇博客系列篇.本篇为: v68.xx 鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基 ...

  8. P3971-[TJOI2014]Alice and Bob【贪心】

    正题 题目链接:https://www.luogu.com.cn/problem/P3971 题目大意 一个\(1\sim n\)的一个排列,设\(a_i\)表示以\(i\)结尾的最长上升子序列长度, ...

  9. 100台机器上海量IP如何查找出现频率 Top 100?

    场景题 有 100 机器,每个机器的磁盘特别大,磁盘大小为 1T,但是内存大小只有 4G,现在每台机器上都产生了很多 ip 日志文件,每个文件假设有50G,那么如果计算出这 100 太机器上访问量最多 ...

  10. Network Analyst Tools(Network Analyst 工具)

    Network Analyst 工具 1.分析 # Process: 创建 OD 成本矩阵图层 arcpy.MakeODCostMatrixLayer_na("", "O ...