1、== 对于基本变量,判断其值是否相同;对于引用对象,判断其指向的内存地址是否相同。
2、equals 只能应用于引用对象,默认和==的作用相同,如果像String一样继承的话,则可以实现自定义的比较。
3、HashCode仅在Hash容器中使用,用于决定将要插入对象的位置。(一般来说,equals相等的两个对象,它们的hashCode也要相等)
package com.demo.test;
import java.util.HashSet;
public class EqualDemo {
static class Person implements Cloneable {
public String idcard;
public Person(String idcard) {
this.idcard = idcard;
}
//super.clone方法是protected的,这里要将它的可见性手动改为public, 并且删除throws CloneNotSupportedException
@Override
public Object clone() {
return new Person(idcard);
}
}
static class NewPerson {
public String idcard;
public NewPerson(String idcard) {
this.idcard = idcard;
}
@Override
public Object clone() {
return new NewPerson(idcard);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof NewPerson) {
if(idcard != null && idcard.equals(((NewPerson) obj).idcard)) {
return true;
}
}
return false;
}
}
static class EvolutionPerson {
public String idcard;
public EvolutionPerson(String idcard) {
this.idcard = idcard;
}
@Override
public Object clone() {
return new EvolutionPerson(idcard);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof EvolutionPerson) {
if(idcard != null && idcard.equals(((EvolutionPerson) obj).idcard)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return idcard.hashCode();
}
}
public static void main(String[] args) {
String str1 = "123";
String str2 = "123";
String str3 = new String("123");
System.out.println(str1 == str2);//true
System.out.println(str1.equals(str2));//true, 这说明对于基本类型变量,equal比较其值,值相同则认为一致
// System.out.println(str1 == 123);//Error: Incompatible operand types String and int
System.out.println(str1.equals(123));//false, 在java中,如果类型不一样,肯定不同。如果是javascript则会自动转化
System.out.println(str1 == str3);//false, 字符串常量在常量池中,而new的字符串存放在堆中,==只比较地址
System.out.println(str1.equals(str3));//true, string复写了equls方法,先比较==,如果不等,再一个一个字符的进行比较
Person p1 = new Person("123");
Person p2 = p1;
Person p3 = (Person) p1.clone();
System.out.println(p1.equals(p2));//true, 这说明对于对象equal比较的是引用类型变量的地址,只有其指向的地址一致,equal才会返回true
System.out.println(p1.equals(p3));//false, 这说明即使引用类型变量的内部变量都一样,地址不一样还是认为是不同的
NewPerson np1 = new NewPerson("123");
NewPerson np2 = (NewPerson) np1.clone();
System.out.println(np1.equals(np2));//true,因为已经覆写了equals方法
/*
在HashMap中,查找key的比较顺序为:
1、计算对象的Hash Code,看在表中是否存在。
2、检查对应Hash Code位置中的对象和当前对象是否相等。
3、如果Hash Code相等,但equals不等,则会放到跟该Hash Code有关地址上的一个链式结构中
*/
HashSet<NewPerson> npSet = new HashSet<NewPerson>();
npSet.add(np1);
npSet.add(np2);
System.out.println(npSet.size());//2, 说明2个对象即使equals返回true,但是还被set判断为不想等
EvolutionPerson ep1 = new EvolutionPerson("123");
EvolutionPerson ep2 = (EvolutionPerson) ep1.clone();
HashSet<EvolutionPerson> epSet = new HashSet<EvolutionPerson>();
epSet.add(ep1);
epSet.add(ep2);
System.out.println(epSet.size());//1, 说明在HashSet中只有hashCode和equals同时相同,才会被判定为一个对象
}
}
————————— 更新于 2018年5月15日 —————————
通常来说,即使 a 与 b 对象的值相同,但是在内存中的地址不相同,那么两个对象会被认为是不一样的。
Java 的 8 种基本类型 (Byte,Short,Integer,Long,Character,Boolean,Float,Double) 除 Float 和 Double 以外,其它 6 种都实现了常量池。
但是它们只在 [ -128, 127] 区间内时,并且只使用等号(=)赋值而不使用new的情况下,才能使用常量池,超过范围则会被存储到堆内存中。
Integer a = 127, b = 127, c = new Integer(127), d = new Integer(127);
System.out.println(a == b); //true
System.out.println(a == c); //false
System.out.println(c == d); //false
a = -128; b = -128;
System.out.println(a == b); //true
a = 128; b = 128;
System.out.println(a == b); //false
a = -129; b = -129;
System.out.println(a == b); //false
Boolean b1 = true, b2 = true, b3 = new Boolean(true);
System.out.println(b1 == b2); //true
System.out.println(b1 == b3); //false
a=1; b = Integer.valueOf(1);
System.out.println(a == b); //true
通过反编译可以看出,使用等号直接赋值,只是调用类的 valueOf 方法的一个语法糖。所以 “常量池” 设在 [ -128, 127] 区间内,在它的源码源码中写明了。
valueOf 的源码如下:
/**
* 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) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache 的源码如下:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the -XX:AutoBoxCacheMax=<size> option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
在server模式下,使用-XX:AutoBoxCacheMax=NNN参数即可将Integer的自动缓存区间设置为[-128,NNN]。这个参数是server模式专有的。
Java 中的 equals() 具有如下特性:
对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
反射性:x.equals(x)必须返回是“true”。
类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。