Java 源码重读系列之 HashMap

本文详细介绍了Java中HashMap的源码实现,从成员变量开始,包括hash()、comparableClassFor()、tableSizeFor()等方法的分析,重点讲解了putVal()和resize()方法的工作原理,以及HashMap如何处理哈希冲突和扩容操作。通过阅读,读者可以深入理解HashMap的内部工作机制。
摘要由CSDN通过智能技术生成

0. 成员变量

首先我们先看一下 HashMap 有哪些成员变量

    /**
     * 默认的初始大小,16,值必须是 2 的幂值
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 最大值,必须是 2 幂值且 小于等于 2 的30 次幂
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;
    /**
     * 默认加载因子,0.75,就是map里的元素值超过 75% 就会触发扩容
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //下面还有三个树化的参数
    //如果哈希值相同的元素超过 8 个,则树化
    static final int TREEIFY_THRESHOLD = 8;
    //树的节点小于 6 个,则转成链表
    static final int UNTREEIFY_THRESHOLD = 6;
    //如果 map 的元素小于 64,即便是 哈希值相同的元素超过 8 个了,也不树化,会扩容
    static final int MIN_TREEIFY_CAPACITY = 64;
复制代码

下面还有一个 Node 类,这个类就是 HashMap 存储元素的容器,其实很简单

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

        //... ... 

}
复制代码

没有多少复杂的内容,类似于链表的Node节点,key、value、next,因为大量的地方都需要用到对象的 hash 值,所以又记录了下 key 的 hash 值。

1. hash()

继续往下看

	//求 key 的哈希值
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
复制代码

也没什么好说的,就是通过对象的 hashCode 计算出一个 int 值。

2. comparableClassFor()

下面有个 comparableClassFor 方法,这个方法的主要是判断入参 x 是否实现了 Comparable 接口。不过写的格式有点紧凑,我们需要展开以下。

	static Class<?> myComparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c = x.getClass();
            // 获取 x 实现的所有接口
            Type[] ts = c.getGenericInterfaces();
            Type[] as;
            Type t;
            ParameterizedType p;
            //如果 x  是 String 类型 直接返回
            if (c == String.class) {
                return c;
            }
            if (ts != null) {
                // 遍历实现的所有接口
                for (int i = 0; i < ts.length; ++i) {
                    t = ts[i];
                    // 如果接口有泛型
                    if (t instanceof ParameterizedType) {
                        p = (ParameterizedType) t;
                        // 如果泛型对象是 Comparable
                        if (p.getRawType() == Comparable.class) {
                            as = p.getActualTypeArguments();
                            // 只有一个泛型参数,且参数类型是 x.class
                            if (as != null && as.length == 1 && as[0] == c) {
                                return c;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
复制代码

举个例子:

class MyTest implements Comparable<MyTest> {}

会返回 MyTest.class

class MyTest implements Comparable<Integer> {}

会返回 null
复制代码

这个方法就结束了,如果还是不能理解,可以将代码复制出来,打个断点跑一下。继续往下看。


    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
    static int compareComparables(Class<?> kc, Object k, Object x) {
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x));
    }
复制代码

这个方法有意思,注释是说,如果 x 是 kc 的类型,返回 k.compareTo(x) (k 是筛选过的比较类)。如果你查找下这个方法的使用地方就会发现,kc 这个参数就是上面 comparableClassFor 返回的类型。

这么一看是不是就清晰了? comparableClassFor(x) 拿到类型,然后传入 compareComparables(kc,k,x) 去比较。

3. tableSizeFor()

下面又是一个方法:

    static final int tableSize
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值