HashMap 的数据结构

目录

  • content
  • append

content

HashMap 的数据结构:

  • 数组 + 链表(Java7 之前包括 Java7)
  • 数组 + 链表 + 红黑树(从 Java8 开始)

PS:这里的《红黑树》与链表都是链式结构。

HashMap 内部维护了一个数组,数组中存放链表的链首或红黑树的树根。

当链表长度超过 8 时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高 HashMap 的性能;在红黑树结点数量小于 6 时,红黑树转变为链表。

下面分别为上面两种数据结构的图示:



【定位算法】

增加、查找、删除等操作都需要先定位到 table 数组的某个索引处。

定位算法为三步:取 key 的 hashCode 值、高位运算、取模运算得到索引位置。(代码如下)

static final int hash(Object key) {
    int h;
    // h = key.hashCode() 第一步 取 hashCode 值
    // h ^ (h >>> 16)  第二步 高位参与运算 Java8 优化了高位算法,优化原理忽略
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

// java7 中这是一个单独的方法,java8 没有了这个方法但是原理依旧
static int indexFor(int h, int length) {
    return h & (length-1); // hash(key) & (length-1)  第三步 取模
}

取模运算h & (length -1)的结果最大值为 length -1,不会出现数组下标越界的情况。

为什么要做高位运算?

如果 hashCode 值都大于 length,而且这些 hashCode 的低位变化不大,就会出现很多冲突,举个例子:

  • 假设数组的初始化容量为 16(10000),则 length -1 位 15(1111)。
  • 假设有几个对象的 hashCode 分别为 1100 10010、1110 10010、11101 10010,如果不做高位运算,直接使用它们做取模运算的结果将是一致的。

如果所有元素中多数元素属于这种情况,将会导致元素分布不均匀,而对 hashCode 进行高位运算能解决这个问题,使高位对低位造成影响改变低位的值,从而变相地使高位也参与运算。

append

【Q】负载因子与性能的关系

负载因子默认值为0.75,意味着当数组实际填充量占比达到3/4时就该扩容了。

负载因子越大,扩容次数必然越少,数组的长度越小,减少了空间开销;这就会导致 hash 碰撞越多,增加查询成本。

默认值0.75在时间和空间成本上寻求一种折衷。



【Q】为什么要扩容

因为随着元素量的增大,hash 碰撞的概率越来越大,虽然使用链地址法能够解决存储问题,但是长长的链表会让 HashMap 失去快速检索的优势,而扩容能解决这个问题。

原文地址:https://www.cnblogs.com/xmsx/p/9750299.html

时间: 2024-10-17 08:47:02

HashMap 的数据结构的相关文章

[转]java 的HashMap底层数据结构

java 的HashMap底层数据结构 HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-value总是会当做一个整体来处理,系统会根据hash算法来来计算key-value的存储位置,我们总是可以通过key快速地存.取value.下面就来分析HashMap的存取. 一.定义 HashMap实现了Map接口,继承AbstractMap.其中Map接口定义了键映射到值的规则,而AbstractM

java 的HashMap底层数据结构

HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-value总是会当做一个整体来处理,系统会根据hash算法来来计算key-value的存储位置,我们总是可以通过key快速地存.取value.下面就来分析HashMap的存取. 一.定义 HashMap实现了Map接口,继承AbstractMap.其中Map接口定义了键映射到值的规则,而AbstractMap类提供 Map 接口的骨干实现,以最

HashMap的数据结构

HashMap的数据结构 数组的特点是:寻址容易,插入和删除困难:而链表的特点是:寻址困难,插入和删除容易.那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法-- 拉链法,我们可以理解为"链表的数组",如图: 从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点.那么这些元素是按照什么样的规则存储到数组中呢.一般情

HashMap底层数据结构

1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 2.    HashMap的数据结构: 在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外.HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体. 从上图中可以看出,HashM

hashmap的数据结构和算法

hashmap的数据结构就是一个哈希表(散列表) hashmap: 1)数组:连续地址,查找迅速,但是占用内存太大 2)链表:地址不是连续的节省空间,查找较数组慢,删除和添加快 集合了两种数据结构的优点 数组的目的:就是根据关键字的key利用散列函数映射地址,此地址就存储在数组中 链表的目的:解决冲突问题,因为不同的关键字根据散列函数映射地址可能会相等,讲最新的插入头部 散列函数: 1).直接定址法 取关键字或关键字的某个线性函数值为散列地址,即: h(key) = key   或 h(key)

Java中HashMap的数据结构

类声明: 概述: 线程不安全: <Key, Value>两者都可以为null: 不保证映射的顺序,特别是它不保证该顺序恒久不变: HashMap使用Iterator: HashMap中hash数组的默认大小是16,增长方式一定是2的指数倍: HashMap的数据结构: 在Java语言中,最基本的结构只有两种,一个是数组,另一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造.HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体. 通过分析这两种数据结构的优劣,才

jdk1.8源码解析:HashMap底层数据结构之链表转红黑树的具体时机

前言 本文从三个部分去探究HashMap的链表转红黑树的具体时机: 一.从HashMap中有关“链表转红黑树”阈值的声明: 二.[重点]解析HashMap.put(K key, V value)的源码: 三.测试: 一.从HashMap中有关“链表转红黑树”阈值的声明,简单了解HashMap的链表转红黑树的时机 在 jdk1.8 HashMap底层数据结构:散列表+链表+红黑树(图解+源码)的 “四.问题探究”中,我有稍微提到过散列表后面跟什么数据结构是怎么确定的: HashMap中有关“链表转

HashMap底层数据结构之链表转红黑树的具体时机

前言 本文从三个部分去探究HashMap的链表转红黑树的具体时机: 1.从HashMap中有关"链表转红黑树"阈值的声明:2.[重点]解析HashMap.put(K key, V value)的源码:3.测试: 一.从HashMap中有关"链表转红黑树"阈值的声明,简单了解HashMap的链表转红黑树的时机 HashMap中有关"链表转红黑树"阈值的声明: /** * 使用红黑树(而不是链表)来存放元素.当向至少具有这么多节点的链表再添加元素时,

jdk1.8 HashMap底层数据结构:深入解析为什么jdk1.8 HashMap的容量一定要是2的n次幂

前言 1.本文根据jdk1.8源码来分析HashMap的容量取值问题: 2.本文有做 jdk1.8 HashMap.resize()扩容方法的源码解析:见下文“一.3.扩容:同样需要保证扩容后的容量是2的n次幂”: 3.目录: 一.jdk1.8中,对“HashMap的容量一定是2的n次幂”做了严格控制 1.默认初始容量 2.使用HashMap的有参构造函数来自定义容量的大小(保证容量是2的n次幂) 3.扩容:同样需要保证扩容后的容量是2的n次幂( jdk1.8 HashMap.resize()扩