Java String 类型的HashCode 源码分析

Java String 类型的HashCode 源码分析

同样是这几天看 HashMap的源码 当 key类型是String类型的时候,需要求出key的hashCode

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

其中 第二段代码 是 key如果不等于null,那么 就用key的hashCode值与Key的hashCode值的高16位做异或操作。因此为了探明 key的hashCode是如何得出的 于是乎咱们继续往下看。这个是 String类型 重写HashCode方法之后的源代码

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

这段代码不是很复杂,可以分析一下。

  1. 第一 从源码中得知,int h=hash 初始值是0 那就是 说 h=0。
  2. 第二 if (h == 0 && value.length > 0) 这个判断的意思就是,初始值如果是字符串的话 就会进入到这个 判断里。
  3. 接着 通过 char val[] = value; 将字符串 变成一个一个字符数组。
  4. 于是 这个循环 映入眼帘
    for (int i = 0; i < value.length; i++) {
    h = 31 * h + val[i];
    }
    分析这个循环可以得出一个这样的 结论,就是 h =31*h+val[i] (字符的Ascii码) 把这个循环展开就会得到一个这样的数学式子
    h=31^(n-1)*val[0]+
    31^(n-2)*val[1]+…+val[n-1]
    那么 意思就是 用 31 作为底数,n(字符串长度)作为指数乘以相应字符的Ascii 码,然后依次累加。这样就会得到了 这个字符串的 hashCode。
  5. 但是这里还有一个问题就是,为什么 要选择 31 这个数字作为 底数呢? 为什么不是 32? 33? 或者是 10? 在这里我是这么思考的
    第一不选择偶数的原因就是 因为hash的宗旨就是为了让数字更加的散列。那么最好选择的这个数字不要被其他数字整除,否则的话就会产生很多的碰撞,因此这也是不选择偶数的一个原因,所以选择的这个数字最好即是质数也是奇数的数字
    第二 31这个数字 在计算的过程中可以可以拆解成 31n=32n-n=n>>5-n 这样做得话,就可以将乘法操作优化成底层的移位操作,加快运算的速率
    第三 那么 如果是这样的话 7N 也可以优化成 8n-n 5也可以优化成 4*n+n 等等 为什么不选择这样的数字呢? 那是因为 我们要记住 Hash的宗旨就是让数字更加的散列,因此乘以一个相对较大的数字 得到的值就会更加的散列,如果底数仅仅是一个 7 或者是 5的话虽然复合 第一和第二个原因 但是 它却不能避免更多的碰撞。 所以综上所述 选择31 这个数字。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值