关于“==”
如果是原始类型,就是直接比较他们的值;
如果是引用类型,比较的就是引用的值。引用的值就是指对象的地址。也就是两个引用指向同一个对象,那么==比较返回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方法实现如下:
- Boolean的两个值true和false都是缓存在内存中的,无需做任何改造,自己new出来的则是另外一个堆内存。
- Byte的256个值全部是缓存在内存中的,自己new出来的则是另外一个堆内存。
- Short和Long两种类型的缓存范围为-128-127,无法调整缓存范围,在实际场景中需要开发者可以根据需要自己做缓存。
- 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; }
-
首先判定对比的对象和当前对象地址。如果相同直接返回true;
-
如果不相同,判定出入对象是否为String,若不是直接返回false;
- 如果为String类型,判断字符串长度,若不一致直接返回false;
- 如果长度相同,循环对比两个字符串的char[]数组。逐个对比字符是否相同,若存在不一致的情况就返回false。
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; }
-
首先判定对比的对象和当前对象地址。如果相同直接返回true;
-
如果不相同,判定出入对象是否为String,若不是直接返回false;
- 如果为String类型,判断字符串长度,若不一致直接返回false;
- 如果长度相同,循环对比两个字符串的char[]数组。逐个对比字符是否相同,若存在不一致的情况就返回false。
关于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[]数组对比一次才能证明两个对象是否一样。