java中对象的equals()方法和hashCode()方法

关于“==”

如果是原始类型,就是直接比较他们的值;

如果是引用类型,比较的就是引用的值。引用的值就是指对象的地址。也就是两个引用指向同一个对象,那么==比较返回true,否则false。

这里需要说明的一个问题是,关于Integer类中一个看起来奇怪的现象。

看int的包装类型Integer类:

        Integer a = 20;
        Integer b = 20;
        Integer c = 200;
        Integer d = 200;
        int e = 200;
        System.out.println(a == b);
        System.out.println(c == d);
        System.out.println(d == e);

输出结果:

true

false

true

原来在Integer类的valueOf方法中默认缓存了-128到127之间的数字。而且在Integer与int进行比较的时候会进行自动拆箱操作,也就是比较他们的数字值。

 

扫描二维码关注公众号,回复: 849490 查看本文章
/**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

       我们可以在JVM启动时设置参数-Djava.lang.Integer.IntegerCache.high=1000来间接设置IntegerCache。high的值,或者通过设置-XX:AutoBoxCacheMax=<size>。那么对于其他原始类型的包装类,是否有同样的情况呢。经过研究发现,这些类型的valueOf方法实现如下:

  1. Boolean的两个值true和false都是缓存在内存中的,无需做任何改造,自己new出来的则是另外一个堆内存。
  2. Byte的256个值全部是缓存在内存中的,自己new出来的则是另外一个堆内存。
  3. Short和Long两种类型的缓存范围为-128-127,无法调整缓存范围,在实际场景中需要开发者可以根据需要自己做缓存。
  4. Float,Double没有缓存,在实际场景中需要开发者可以根据需要自己做缓存。
       

关于equals()方法

equals()方法首先在Object类中提供了默认实现:

 

  public boolean equals(Object obj) {
        return (this == obj);
    }
 如果不去重写equals()方法,并且在父类列表中都没有重写过equals()方法,那么equals()方法默认就是比较对象地址。一般equals()方法是希望子类去重写的,以实现对比值的功能。
JDK8中String类equals()方法如下:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  1.  首先判定对比的对象和当前对象地址。如果相同直接返回true;
  2. 如果不相同,判定出入对象是否为String,若不是直接返回false;

  3. 如果为String类型,判断字符串长度,若不一致直接返回false;
  4. 如果长度相同,循环对比两个字符串的char[]数组。逐个对比字符是否相同,若存在不一致的情况就返回false。
JDK8中String类equals()方法如下:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  1.  首先判定对比的对象和当前对象地址。如果相同直接返回true;
  2. 如果不相同,判定出入对象是否为String,若不是直接返回false;

  3. 如果为String类型,判断字符串长度,若不一致直接返回false;
  4. 如果长度相同,循环对比两个字符串的char[]数组。逐个对比字符是否相同,若存在不一致的情况就返回false。
 首先判定对比的对象和当前对象地址。如果相同直接返回true;

关于hashCode()方法

一般equals()方法是与hashCode()方法成对出现的。hashCode()方法提供了对象的hashCode值,是一个native方法:它的返回值默认是与System.identityHashCode(obj)方法相同。注意,它的值绝对不是代表对象的地址。

  public native int hashCode();
 hashCode只能说是标志对象,这样在hash算法中可以将对象相对离散开,这样就可以在查找数据时根据key快速缩小范围。但我们不能说hashCode值一定是唯一的(不同对象实例可能有相同的hashCode值,这个很好理解,子类可以重写hashCode()方法,当然有可能导致这个结果。)。因此在hash算法应用中在定位到具体链表后,需要进一步循环链表,然后通过equals方法来对比一下key值是否一样。 前面说到hashCode()方法是可以重写的,重写后的方法将决定它在hash相关数据结构中分布情况,通常我们尽可能将对象相对离散开。 下面看看JDK8中String类的hashCode()方法:
/**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    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;
    }
 从注释中我们知道String对象的hashCode计算算法为:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
其中s[i]为字符串中对应位置字符,n为字符串长度。这个hashCode是根据char[]数组计算出来的,不同的String完全可以算出相同的hashCode值。因此即使hashCode值一样,也不能证明两个String是一样的。还需要再次遍历char[]数组对比一次才能证明两个对象是否一样。  

猜你喜欢

转载自zhang-kai-123.iteye.com/blog/2202872