HashCode的深思

hash值计算方法深思

  1. 项目中用到了RecyclerView的DiffUtil,其中就涉及到equals来判断两个类变量是否相同,散列结构存储又涉及到了Hash值计算。其中hashCode方法内部如何实现自己定义的hash值。当散列结构存储存储对象时,难免会遇到A与B对象的hash值相同的时候,hash值相同后用equals方法去判断是否是为同一个变量,如果equals返回true,则代表散列结构存储中已经有该对象,所以会舍弃掉;如果equals返回false,则将该对象添加进散列结构存储中。

hashCode与equals的关系

  1. hashCode相同的情况下,equals不一定返回true。(例如A与B发生hash碰撞,但他们是两者完全不同的对象)。
  2. 两个对象equals返回true的时候,则hashCode必须返回相同的int值。
  3. 两个对象hashCode返回不同的int值,equals一定会返回false。
    参考文献

String

hasCode方法
  1. 为了方便理解hashCode的计算,我们下面用从String源码分析一波。
    首先我们先看一眼源码,再后面一点点进行分析。
public int hashCode() {
        //hash初始化是0
        int h = hash;
        final int len = length();
        if (h == 0 && len > 0) {
            for (int i = 0; i < len; i++) {
                h = 31 * h + charAt(i);
            }
            hash = h;
        }
        return h;
}
  1. 上面hash值的计算中为什么我们要选择31作为因子进行计算呢?原因选择数字31是因为它是一个奇质数,如果选择一个偶数会在乘法运算中产生溢出,导致数值信息丢失,因为乘二相当于移位运算。选择质数的优势并不是特别的明显,但这是一个传统。同时,数字31有一个很好的特性,即乘法运算可以被移位和减法运算取代,来获取更好的性能:31 * i == (i << 5) - i,现代的 Java 虚拟机可以自动的完成这个优化。
    这里参考文献:为什么选择31
  2. 看完文献可以发现,选择31是尽可能避免在散列存储中计算hash值发生hash碰撞,假如很不幸发生了hash碰撞那又该如何判断String是否完全相同呢?那就要看接下来介绍的equals方法了。
equals方法
  1. 可以从String方法中观察到,是否需要往常量池需要添加新的String,hashCode与equals都是必不可少的,其中当两个不同的String的hashCode返回值相同时,会用比较传统且安全的方法对比两个对象是否完全相同。这个传统的方法就是逐个比较每个字符是否相同,如果遇到一个不同立即返回false。
public boolean equals(Object anObject) {
        if (this == anObject) {
            //如果比较的对象是当前对象
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = length();
            if (n == anotherString.length()) {
                int i = 0;
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

总结

  1. 学无止境,抽空去学习自己感兴趣的Android源码的知识,一点一滴学习,你会发现在需要用到的时候,你能把很多知识点给串联起来,这才是成长的关键。

你可能感兴趣的:(HashCode的深思)