equals源码:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
public boolean equals(Object obj) {
return (this == obj);
}
可见,源码中的equals不重写的话,就是==
先重写equals方法:
加入有一个user类,忽略掉id属性,比较name和age,如果两个相等,则视为true。
重写equals方法需要遵循Java如下规则,否则编码行为会难以揣测:
- 自反性:对于任意的对象x,x.equals(x)返回true(自己一定等于自己);
- 对称性:对于任意的对象x和y,若x.equals(y)为true,则y.equals(x)亦为true;
- 传递性:对于任意的对象x、y和z,若x.equals(y)为true且y.equals(z)也为true,则x.equals(z)亦为true;
- 一致性:对于任意的对象x和y,x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也均为true,前提条件是没有修改x也没有修改y;
- 对于非空引用x,x.equals(null)永远返回为false。
写一下单元测试:
public class userTest {
@Test
public void equals1() {
user x = new user(0,"刘三",18);
user y = new user(4,"刘三",18);
user z = new user(0,"刘三",18);
/** 自反性:对于任意的对象x,x.equals(x)返回true(自己一定等于自己);*/
assertTrue(x.equals(x));
/** 对称性:对于任意的对象x和y,若x.equals(y)为true,则y.equals(x)亦为true;*/
assertTrue(x.equals(y));
assertTrue(y.equals(x));
/**
传递性:对于任意的对象x、y和z,若x.equals(y)为true且y.equals(z)也为true,则x.equals(z)亦为true;*/
assertTrue(x.equals(y));
assertTrue(y.equals(z));
assertTrue(x.equals(y));
/**
一致性:对于任意的对象x和y,x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也均为true,前提条件是没有修改x也没有修改y;*/
assertTrue(x.equals(y));
/**
对于非空引用x,x.equals(null)永远返回为false。*/
assertFalse(x.equals(null));
}
}
代码
public class user {
private int id;
private String name;
private int age;
user(int id,String name,int age){
this.id =id;
this.name=name;
this.age =age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
user user = (user) o;
return
age == user.age &&
Objects.equals(name, user.name);
}
}
hashcode 方法的通用约定
- 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,它必须始终如一地返回 同一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同,即这个应用程序这次执行返回的整数与下一次执行返回的整数可以不一致。
- 如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。
- 如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高散列表(hash table)的性能。
单元测试代码
/* - 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,
它必须始终如一地返回 同一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同,
即这个应用程序这次执行返回的整数与下一次执行返回的整数可以不一致。
*/
assertTrue(x.hashCode()==x.hashCode()&&x.hashCode()==x.hashCode());
/*- 如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。*/
assertFalse(x.hashCode()==z.hashCode());
assertFalse(x.hashCode()==y.hashCode());
/* - 如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,
不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,
有可能提高散列表(hash table)的性能。
*/
运行通过,发现不满足第二条。即两个相等的对象,hashcode不一样。
再看如下代码片段
HashMap<user,Integer> m = new HashMap<user,Integer>();
m.put(x,10);
m.put(y,20);
m.put(z,30);
System.out.println(m.get(new user(0,"刘三",18)));
运行结果是null;
- map 中源码
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
get中比较的是hashcode。
而hashcode不相等。
重写hashCode()
@Override
public int hashCode() {
int result = 17;
result = 31*result + name!=null?name.hashCode():0;
result = 31*result + age;
return Objects.hash(name, age);
}
满足第二条,且System.out.println(m.get(new user(0,“刘三”,18)));的输出为30