HashMap源码解析(基于JDK1.8)

散列是一种用于以常数平均时间执行插入、删除和查找的技术。HashMap是基于哈希表的Map接口实现,该实现提供了所有可选的映射操作,并允许使用 空值和空键。(HashMap 类大致等同于Hashtable,除了它是不同步的并且允许为空值。)这个类不能保证Map的顺序; 特别是,它不能保证顺序会随着时间的推移保持不变。

下面就开始我们的源码分析吧:

老规矩,先贴一张UML图:

这里写图片描述

①:HashMap直接继承自AbstractMap抽象类,在AbstractMap已经实现了Map接口中的所有方法,只有public abstract Set<Entry<K,V>> entrySet()方法是抽象,由子类实现自己的entrySet()方法。在HashMap中的一个实例域也和此方法有关联。后面会分析。
②HashMap实现了Map接口,Map中不能使用Map作为key,但Map可以作为value,但是不建议这样做。下图是JDK1.8官方文档给出的解释。
这里写图片描述
③实现了Cloneable表明HashMap支持可克隆。
④实现了Serializable接口,表明HashMap支持序列化。

好啦,终于到了源码分析的部分了:


下面是HashMap存储结构图:
(该图来自美团技术团队,我分析HashMap的时候,也参考了这里的内容。
这里写图片描述
老规矩,我们还是先分析HashMap中的实例域部分(也就是HashMap的属性),但是HashMap比较复杂,它的实例域部分的类型是HashMap中的一个内部类,而对应的内部类又实现了Map接口中声明的一个接口(Map.Entry<K,V>),是不是听起来有点诡异(⊙o⊙)…,为了更好的学习HashMap,我们还是从最外层(也就是Map.Entry<K,V>)开始看起吧。其实很简单的….

1:我们先看看HashMap中的哈希桶(Node<K,V>),它是HashMap中的一个静态内部类,它实现了Map.Entry<K,V>接口,该接口是Map接口内声明的一个内部接口,它表示Map中的一个实体(也就是一个键值对(key-value对))

2:我们先看看Map.EnTRY<K,V>这个接口的源码:
从代码中可以看出,我们可以直接通过该Entry对象获取相应的键和值。它提供了很多有用的方法,代码很简单。不过最后提供了JDK1.8之后新增的方法,主要基于Stream做操作,目前我也仅仅停留在会用阶段,源码分析不来(⊙o⊙)…(后面关于Stream的部分的分析直接跳过(艹皿艹 ))

 interface Entry<K,V> {

        K getKey();

        V getValue();

        V setValue(V value);

        boolean equals(Object o);

        int hashCode();

        public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }
        public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getValue().compareTo(c2.getValue());
        }
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
        }
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
        }

3:我们先看一下HashMap中的第一个字段:transient Node<K,V>[] table,它就是我们的哈系桶数组。接下来给出Node<K,V>的源码。

4:Node<K,V>(哈系桶),它实现了Map.Entry<K,V>接口,下面给出源码:
下面我们逐个介绍一下它的字段的作用:
hash:主要用来定位当前Node在数组位置的索引。
key:键值对中的键。
value:键值对中的值。
Node<K,V> next指代链表中的下一个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;
        }
        //该方法实现Map.Entry<K,V>接口。
        public final K getKey()        { return key; }
        //该方法实现Map.Entry<K,V>接口。
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
        //该方法实现Map.Entry<K,V>接口。设置新值,并返回旧值。
        
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值