equals()和hashCode() 详细学习
- Object类是每个类的父类,所有的对象实现了Object中定义的方法。
equals()、hashCode()
它们是java.lang.Object
中的方法。
equals()
Object类中的equals()
public boolean equals(Object obj) {
return (this == obj);
}
使用等号判断两个对象是否相等,对象在内存中的表现形式为标识(内存地址)、状态(数据)。
等号==
和equals()
相等问题
- 等号比较的是两个对象的内存地址是否相等。
- equals()比较的是对象的内容是否相等。
例子1
String a = "123";
String b = "123";
System.out.println(a==b?"yes":"no");
- a和b的创建方式,可能创建 一个对象也有可能不创建,如果赋值的字符串在java String池中不存在,会在String池中创建一个
a="123"
的对象,然后把a、b指向这个内存地址,之后用这种方式创建,始终只有一个地址被分配。
例子1中输出的结果应该是yes,因为a==b判断的是对象的地址是否相等,综合分析此时a和b的地址相等。
例2
String c = new String("123");
String d = new String( "123");
System.out.println(c==d?"yes":"no");
new String()
至少会创建一个对象,也有可能创建两个,会在堆中创建一个c的对象,同时如果这个字符串在String池中不存在。会在String池中创建"123"
这个对象。
例2中输出的结果是No,这和new String()
有关。此时用==判断时候两个对象的地址不同,所以值为no.
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;
}
hashCode()
hashCode()方法给对象返回一个hash code值,这个方法被用于hash tables。
-
如果根据
equal(Object)
方法是相等的,那么调用二者各自的hashCode()
方法必须产生同一个int的结果。 -
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
Object类中hashCode()方法的定义
public native int hashCode();
- hashCode的存在主要用于查找的快捷性,如Hashtable、HashMap等,hashCode是用来在散列存储结构中确定对象存储地址的。
重写equals()和hashCode()
public class User {
private int id;
private String name;
private String address;
public User(int id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
return id == user.id &&
Objects.equals(name, user.name) &&
Objects.equals(address, user.address);
}
@Override
public int hashCode() {
return Objects.hash(id, name, address);
}
// setget()省略
}
如果只重写equals()方法那么num为null。user指向的对象和map.get()中new 的对象是两个对象,他们的存储地址不同。
@Test
public void test1(){
User user = new User(1,"张三","test1");
System.out.printf(String.valueOf(user.hashCode()));
HashMap<User,Integer> map = new HashMap<>(16);
map.put(user,23);
System.out.print("=======");
Integer num = map.get(new User(1, "张三", "test1"));
System.out.printf(String.valueOf(num));
}
HashMap的get()
- get()方法,如果getNode()为Null 则get()返回null。
- getNode()
- hash是key的hash,形参key就是key
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;
// 成员变量table不为空,且table的长度大于0,并且经过hash函数计算的存储单元不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// 第一个节点的hash值等于hash 且第一个节点的key等于key
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);
}
}
// 找不到返回null
return null;
}