something_about_hashCode

(注 : 此blog主要是为了加深java 源码的认知度的记录java

hashCode是一个返回hash(散列码)的方法,hash 就是用于区分对象的标志,就是相似于人类的基因,咱们的母类Object 就拥有这样的hashCode方法来返回hash值,这个在java 集合类的Map中是核心,因此map玩的溜,就得搞清楚键值对,键值对的重点就是hash算法

了解了hash以后,咱们再来看看java 类库中的一个比较特殊的基本数据类型 String ui

先来看看代码this

 

 1 /**
 2  *  hashCode
 3  */
 4 package test;
 5 
 6 /**
 7  * @author Amory.Wang
 8  * Question : 
 9  * 2017年9月4日下午7:18:35
10  */
11 public class Test {
12     public static void main(String[] args) {
13         String s = new String("s");
14         String t = new String("s");
15         
16         System.out.println(s.hashCode() == t.hashCode());
17         System.out.println(s.hashCode());
18         System.out.println(t.hashCode());
19     }
20 }

 

第一个syso 打印的是什么?spa

有点java 基础的猿都应该知道s 和 t 是两个不一样的对象,这一点的证实能够用 == 来打印可看,因此答案应该是false,可是然而,倒是true 翻译

你可能会反问 :wtf?你不是说不一样的对象有本身的hash吗,本身的基因吗,你在欺骗我吗?code

其实否则,因此看看源码就知道,String 里面是实现了hashCode()的方法的, 附码以下:对象

 1 /**
 2      * Returns a hash code for this string. The hash code for a
 3      * <code>String</code> object is computed as
 4      * <blockquote><pre>
 5      * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 6      * </pre></blockquote>
 7      * using <code>int</code> arithmetic, where <code>s[i]</code> is the
 8      * <i>i</i>th character of the string, <code>n</code> is the length of
 9      * the string, and <code>^</code> indicates exponentiation.
10      * (The hash value of the empty string is zero.)
11      *
12      * @return  a hash code value for this object.
13      */
14     public int hashCode() {
15         int h = hash;
16         if (h == 0 && value.length > 0) {
17             char val[] = value;
18 
19             for (int i = 0; i < value.length; i++) {
20                 h = 31 * h + val[i];
21             }
22             hash = h;
23         }
24         return h;
25     }

 

我标记的红色重点看到没有 ?! 先解释一下 在String 类中定义的hash  和 value 以下 : blog

/** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

hash 的默认是 0 , value 是建立对象的时候输入的参数,因此上面就是 String 类中产生hash值的方法,再来读一读这个源码方法,很明显能够看出,hash主要的值是有val的值决定的,源码

h = 31 * h + val[i];

而val 就是咱们传入的参数嘛,这下清楚了吧,也就是说,其实String的hashCode返回的hash值直接关键就是咱们传入的参数值,参数值同样,ok,产生的hash就是同样的。

爱问问题的人(好比我,哈哈,开个玩笑)可能会问,这个hash的生成前面不是还有一个乘法的操做吗?并且乍一看尚未意义,由于定义了h默认为0,乘起来就是0啊?

嗯,java组织毕竟都是大神,怎么会容忍没有意义的代码,这里其实要涉及到了计算机基础的知识,关于乘法的操做,(简单的讲一下啊,毕竟要深刻那可仍是一本书的知识) 计算机的5大部件之一,运算器,(CPU是由运算器和控制器组成的)就是执行运算功能的,尚未智能到能够想人脑同样来计算乘法操做,而是由减法或者逻辑运算来代替的,因此这里的乘法,再翻译一下就是 这样的

31 * h == (h << 5) - h

为何要作这个看上去画蛇添足的动做?具体本身百度,简单说就是这样产生的hash值冲突不会不少,更加容易定位。

若是你使用 31,33, 37,39 和 41 这几个数值,将其应用于 hashCode 的算法中,每个数字对超过 50000 个英语单词(由两个 Unix 版本的字典的并集构成)产生的 hash 只会产生少于 7 个的冲突。知道了这个以后,Java 大多数的发行版均会使用这几个数值之一的事实对你也不会显得奇怪了。


某乎上复制的。。。

 

再来看下面的代码

/**
 *  hashCode
 */
package test;

/**
 * @author Amory.Wang
 * Question : 
 * 2017年9月4日下午7:18:35
 */
public class Test {
    public static void main(String[] args) {
        String s = new String("s");
        String t = new String("s");
        
        System.out.println(s.hashCode() == t.hashCode());
        System.out.println(s.hashCode());
        System.out.println(t.hashCode());
        
        StringBuilder sb = new StringBuilder("s");
        StringBuilder sb1 = new StringBuilder("s");
        System.out.println(sb.hashCode() == sb1.hashCode()); System.out.println(sb.hashCode()); System.out.println(sb1.hashCode());
    }
}

这个sb 是否是相等的呢?stringBuilder是否是和String 同样的呢?答案显然是 不是的,由于StringBuilder里面没有重写hashCode的方法,因此直接调用的是咱们伟大的母类的Object的hashCode的方法,这里面的实现就是根据对象储存的位置有关,你家在哪,你的hash就是啥,一样,通常的对象,也是如此,因此通常的对象hash都是不同的,总结一下String 类就是想搞特殊。。。开个玩笑

本站公众号
   欢迎关注本站公众号,获取更多信息