「深度剖析」一文搞定!从初识HashMap到剖析实现原理

1. 前言

HashMap是Java中最常用的数据结构之一,它提供了一种键值对映射的存储方式,可以非常快速地进行数据的查找和插入操作。在Java的集合框架中,HashMap是使用最广泛的一种数据结构,几乎在所有的Java项目中都会用到。

本文将从基础概念、数据结构、源码实现等方面,深入分析HashMap的实现原理,帮助读者更好地理解HashMap的使用和优化。

2. 基础概念

HashMap是一种基于哈希表的数据结构,它允许将键和值进行映射,键和值都可以是任意类型的对象。在HashMap中,每个键都会对应一个唯一的值,而且键和值之间的映射关系是一一对应的,即每个键只能对应一个值。

HashMap的基本操作包括插入、查找、删除等,其中插入和查找操作的时间复杂度都是O(1),即常数时间,非常高效。这是因为HashMap内部采用了哈希表的数据结构,可以通过哈希函数将键转换成对应的哈希值,并将哈希值对数组长度取模后,得到键值对在数组中存储的位置。由于哈希函数的设计,不同的键的哈希值是不同的,因此不同的键值对可以被存储在不同的位置上,避免了冲突。

但是,由于哈希函数的设计难度较大,如果哈希函数不够好,就容易导致冲突。当两个不同的键的哈希值相同,就会发生冲突。在HashMap中,可以使用链表或红黑树等数据结构来解决冲突。

3. 数据结构

在JDK1.7中,HashMap的数据结构是由数组和链表组成的,称为“数组+链表”结构。具体来说,HashMap内部维护了一个Entry数组,每个Entry节点包含了键值对信息和指向下一个节点的指针,如果同一个数组位置上有多个节点,就会形成一个链表。

当插入一个新的键值对时,首先会根据键的哈希值计算出数组位置,如果该位置上已经有了一个节点,就需要遍历链表,查找是否已经有相同键的节点。如果没有找到相同键的节点,就将新节点插入到链表的末尾。

在JDK1.8中,HashMap的数据结构发生了变化,采用了“数组+链表/红黑树”结构。具体来说,当链表长度超过8时,链表会自动转化为红黑树,以提高查找效率。当红黑树节点个数小于6时,红黑树会自动转化为链表,以避免频繁的树形结构操作。

4. 源码实现

在JDK1.7中,HashMap的源码实现主要包括以下几个类:

  • HashMap:HashMap的主要类,实现了Map接口,提供了键值对的存储、查找、删除等操作。
  • Entry:HashMap中的节点类,包含了键值对信息和指向下一个节点的指针。
  • HashIterator:HashMap的迭代器类,实现了Iterator接口,可以遍历HashMap中的所有键值对。

在JDK1.8中,HashMap的源码实现也包括以上三个类,但是在实现方式上有所不同。在JDK1.8中,HashMap的主要类是Node,而不是Entry。Node类继承了Map.Entry接口,并包含了键值对信息、指向下一个节点的指针、节点的哈希值等信息。此外,HashMap中还引入了红黑树的实现类TreeNode,用于存储链表转换成红黑树后的节点信息。

HashMap的源码实现中,最重要的方法之一是put方法,它用于插入新的键值对。在put方法中,会先根据键的哈希值计算出数组位置,然后在该位置上查找是否已经有相同键的节点。如果没有找到相同键的节点,就会创建一个新的Entry节点,并将其插入到链表的末尾。如果已经存在相同键的节点,就将其值更新为新的值。如果链表长度超过了8,就会将链表转换成红黑树。在插入节点时,HashMap会自动调整数组大小,以保证哈希表的负载因子在0.75以下,避免哈希表过度填充。

除了put方法,HashMap还包括了其他常用的方法,例如get方法、remove方法、size方法等。在JDK1.8中,HashMap还引入了一些新的方法,例如forEach、replace等方法,支持更多的函数式编程操作。

下面我将通过代码来讲解HashMap的实现原理。
在JDK1.7中,HashMap的源码实现中,Entry类是HashMap的节点类,它包含了键值对信息和指向下一个节点的指针。在HashMap中,数组的每个元素都是一个Entry节点,当哈希值相同时,会将新的元素插入到链表的末尾。

下面是Entry类的定义:

class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    int hash;

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

    public final K getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

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

    public final boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        Object k1 = getKey();
        Object k2 = e.getKey();
        if (k1 == k2|| (k1 != null && k1.equals(k2))) {
            Object v1 = getValue();
            Object v2 = e.getValue();
            if (v1 == v2 || (v1 != null && v1.equals(v2)))
                return true;
        }
        return false;
    }

    public final int hashCode() {
        return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
    }

    public final String toString() {
        return getKey() + "=" + getValue();
    }
}

在JDK1.8中,HashMap的源码实现中,Node类是HashMap的节点类,它继承了Map.Entry接口,并包含了键值对信息、指向下一个节点的指针、节点的哈希值等信息。此外,HashMap中还引入了红黑树的实现类TreeNode,用于存储链表转换成红黑树后的节点信息。

下面是Node类的定义:

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public finalK getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

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

    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }

    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

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

5. JDK1.7和JDK1.8的不同点

在JDK1.7和JDK1.8中,HashMap的实现方式有所不同。在JDK1.7中,HashMap的数据结构是由数组和链表组成的,而在JDK1.8中,HashMap的数据结构则是由数组和链表/红黑树组成的。当链表长度超过8时,链表会自动转化为红黑树,以提高查找效率。当红黑树节点个数小于6时,红黑树会自动转化为链表,以避免频繁的树形结构操作。

总结

本文从基础概念、数据结构、源码实现等多个方面,深入分析了HashMap的实现原理,并区分了JDK1.7和JDK1.8的不同点。HashMap作为Java中最常用的数据结构之一,在实际开发中应用广泛,深入理解其实现原理对于优化代码性能和解决问题具有重要意义。同时,ConcurrentHashMap作为HashMap的线程安全版本,也是Java并发编程中经常使用的数据结构,需要掌握其实现原理和使用方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容