前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >HashMap的底层实现-JDK1.8之前

HashMap的底层实现-JDK1.8之前

原创
作者头像
代码小李
发布于 2025-01-30 10:31:13
发布于 2025-01-30 10:31:13
700
举报

在JDK 1.8之前,HashMap的底层实现主要基于数组+链表数据结构。具体来说,HashMap内部维护了一个数组,数组的每个元素都是一个单向链表的头节点。当存储键值对时,HashMap会根据键(key)的哈希值计算出该键值对应该存放在数组的哪个位置,如果该位置已经有其他键值对,则新的键值对会被添加到该位置的链表中。

主要特点

  1. 数组HashMap内部使用一个数组来存储数据,数组的每个元素是一个Entry对象,EntryHashMap的一个静态内部类,表示键值对。
  2. 链表:当多个键值对的哈希值相同(即发生了哈希冲突),这些键值对会被存储在同一个数组位置的链表中。
  3. 哈希函数HashMap使用键的hashCode方法计算哈希值,并通过一定的算法将哈希值转换为数组的索引。
  4. 扩容机制:当HashMap中的元素数量超过某个阈值(通常是数组长度乘以负载因子,默认负载因子为0.75)时,HashMap会自动扩容,通常将数组的长度扩大一倍,并重新计算所有键值对的位置。

具体实现

代码语言:java
AI代码解释
复制
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    // 默认初始容量
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16

    // 最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认负载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 存储键值对的数组
    transient Entry<K,V>[] table;

    // 键值对的条目数
    transient int size;

    // 阈值,当实际大小超过此值时,会触发扩容
    int threshold;

    // 负载因子
    final float loadFactor;

    // 内部类,表示键值对
    static class Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final int hashCode() {
            return (key == null   ? 0 : key.hashCode()) ^
                   (value == null ? 0 : value.hashCode());
        }

        public final String toString() {
            return key + "=" + value;
        }
    }

    // 构造函数
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        setThreshold(initialCapacity);
    }

    // 计算阈值
    private void setThreshold(int initialCapacity) {
        threshold = (int)(initialCapacity * loadFactor);
    }

    // 扩容方法
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

    // 重新计算键值对的位置
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while (null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

    // 计算哈希值
    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    // 计算数组索引
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
}

总结

在JDK 1.8之前,HashMap的底层实现主要依赖于数组和链表的组合,通过哈希函数计算键值对的存储位置,并通过链表解决哈希冲突。当元素数量超过阈值时,会进行扩容操作,以保证性能。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
HashMap的底层实现-JDK1.8之后
在JDK 1.8及之后的版本中,HashMap的底层实现进行了优化,主要改进了处理哈希冲突的方式。具体来说,当链表长度超过一定阈值(默认为8)时,链表会转换为红黑树,以提高查找效率。这种优化使得HashMap在处理大量哈希冲突时性能更好。
代码小李
2025/01/30
1200
JDK1.8源码(七)——java.util.HashMap 类
  本篇博客我们来介绍在 JDK1.8 中 HashMap 的源码实现,这也是最常用的一个集合。但是在介绍 HashMap 之前,我们先介绍什么是 Hash表。 1、哈希表   Hash表也称为散列表,也有直接译作哈希表,Hash表是一种根据关键字值(key - value)而直接进行访问的数据结构。也就是说它通过把关键码值映射到表中的一个位置来访问记录,以此来加快查找的速度。在链表、数组等数据结构中,查找某个关键字,通常要遍历整个数据结构,也就是O(N)的时间级,但是对于哈希表来说,只是O(1)的时间级。
IT可乐
2018/04/17
9030
JDK1.8源码(七)——java.util.HashMap 类
深入解析HashMap原理(基于JDK1.8)
之前经常用HsahMap但是从未了解过底层的实现原理,今天就基于jdk1.8来研究一下HashMap的底层实现。
xcbeyond
2020/03/25
3510
深入解析HashMap原理(基于JDK1.8)
集合详解(四)----HashSet和HashMap源码剖析(JDK1.7)
当初始化一个HashSet的时候,HashSet的底层实现其实是HashMap:
令仔很忙
2018/09/14
6160
集合详解(四)----HashSet和HashMap源码剖析(JDK1.7)
深入浅出理解HashMap1.8源码设计思想&手写HashMapV1.0
数组:采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查找等方式,可将查找复杂度提高为O(logn);对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度也为O(n)
须臾之余
2019/12/03
7310
深入浅出理解HashMap1.8源码设计思想&手写HashMapV1.0
JDK1.7 HashMap源码学习
构造函数和相关参数 /** * 默认初始容量 16,必须是2的幂次方 * 为什么必须是2的幂次方 * */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 /** * 最大容量 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认负载因子 */
Li_XiaoJin
2022/06/10
6700
JDK1.7 HashMap源码学习
迟到一年HashMap解读
HashMap和List这两个类是我们在Java语言编程时使用的频率非常高集合类。“知其然,更要知其所以然”。HashMap认识我已经好多年了,对我在工作中一直也尽心尽力的提供帮助。我从去年开始就想去它家拜访来着,可是经常因为各种各样的原因让其遗忘在路过的风景中。(文章大部分源码基于jdk1.7)。
静默加载
2020/05/29
4340
HashMap实现原理及源码分析
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。 HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
一觉睡到小时候
2019/07/03
4290
HashMap实现原理及源码分析
HashMap 与 ConcurrentHashMap 底层实现
我们存放的 hashMap 都会封装成一个节点对象 Entry(key,value),然后将此节点对象存放到一个数组中,存放前首先需要确定存放的数组下标:① 通过 hash(key) 算法得到 key 的 hashcode,并通过 hashcode的高16位和低16位进行异或操作(如果两个相应bit位相同,则结果为0,否则为1)得到32位的 int值,首先将高16位无符号右移16位与低十六位做异或运算。如果不这样做,而是直接做&运算(相同位的两个数字都为1,则为1;若有一个不为1,则为0)那么高十六位所代表的部分特征就可能被丢失 将高十六位无符号右移之后与低十六位做异或运算使得高十六位的特征与低十六位的特征进行了混合得到的新的数值,这样高位与低位的信息都被保留了 。② int值再与(数组长度-1:底位全为1,高位全为0)进行位运算,获取要存放的下标;③ 如果②中得到相同的值时,判断 key值是否相同,如果相同则新value替换旧value。如果key不相同,将value以链表的形式存放在同一个数组下标下,为了提高存放的速度,新的数据,将存放在原链表的头部。即新数据的 next 指向链表的头元素即可。需要注意的是,每次给链表的头部插入一个新的元素之后,需要将链表的头元素赋值给 table 的下标值。代码展示为 :
Java架构师必看
2021/05/14
4240
HashMap 与 ConcurrentHashMap 底层实现
Carson带你学Java:深入源码解析HashMap 1.8
在了解 如何计算存放数组table 中的位置 后,所谓 知其然 而 需知其所以然,下面我将讲解为什么要这样计算,即主要解答以下3个问题:
Carson.Ho
2022/03/25
5010
Carson带你学Java:深入源码解析HashMap 1.8
去哪面试都会问的HashMap
HashMap可以说是面试的重中之重,去10家公司面试,8家都会问道,为什么大家都爱用HashMap打开话题?
Java识堂
2020/03/17
4650
去哪面试都会问的HashMap
JDK1.8源码分析之HashMap
HashMap是一种Map,HashMap仅是一种Map的实现版本,下面的图片展示了java中Map的一些实现版本:
九州暮云
2019/08/21
3690
java HashMap源码解析
1.Map是一个接口,他是key-value的键值对,一个map不能包含重复的key,并且每一个key只能映射一个value;
用户7886150
2021/04/23
3380
jdk1.7hashMap源码分析
所以,1.7和1.8的hashmap到底有哪些不同呢:     1.hash的取值算法不同     2.求数组下标的算法不同     3.1.8的实体是Node继承了entry,链表长度大于8的时候转换为红黑树。
小小明童鞋
2019/03/12
4760
jdk1.7hashMap源码分析
从代码层读懂HashMap的实现原理
概述  Hashmap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。它的key、value都可以为null,映射不是有序的。       Hashmap不是同步的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。 Map map = Collections.synchronizedMap(new HashMap()); HashMap 中两个重要
xiangzhihong
2018/02/06
1.4K0
从代码层读懂HashMap的实现原理
Java集合深度解析之HashMap
HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。 HashMap是非线程安全的,只
互扯程序
2018/03/26
1K0
Java集合深度解析之HashMap
HashMap(JDK1.8)源码+底层数据结构分析
HashMap 主要用来存放键值对,它基于哈希表的Map接口实现,是常用的Java集合之一。
黑洞代码
2021/02/09
2620
HashMap源码分析 - JDK7和JDK8有什么区别
前几天的文章中对JDK8的HashMap源码进行了分析,这篇文章是基于JDK8的基础上来分析下与JDK7的HashMap的区别。以下的源码主要为JDK7中HashMap的源码。
虞大大
2020/08/26
3370
HashMap源码分析 - JDK7和JDK8有什么区别
基于JDK8的HashMap详解
HashMap是程序员使用频率较高的一种用于映射(键值对)处理的数据类型,随着JDK(Java Development Kit)版本的更新,HashMap也在不断被优化。其中JDK1.8在HashMap底层引入了红黑树的数据结构并对其扩容进行了优化等。本文将结合JDK1.7与JDK1.8对HashMap进行分析,浅析HashMap在JDK1.8中的改进。
Java阿呆
2020/11/04
4260
基于JDK8的HashMap详解
HashMap内部原理解析HeaderHashMap 必知源码分析Java 1.8 中 HashMap 的不同Footer
Header HashMap 在平时 Java/Android 开发中,是绝大多数开发者都普遍使用的集合类。 它内部是基于哈希表实现的键值对存储,继承 AbstractMap 并且实现了 Map 接口。 而对于它的 get/put 使用方法相信大家都已经到了炉火纯青的地步。虽然都会用,却可能没有好好深入探讨过 HashMap 内部的实现原理。正好趁着有时间,今天就给大家一步步地解析 HashMap 的内部实现原理。 在这就基于了 Java 1.7 的源代码来讲解了,Java 1.8 的 HashMap 源码
俞其荣
2018/05/21
6300
相关推荐
HashMap的底层实现-JDK1.8之后
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档