我们都知道判断一个基本数值类型时,就很简单,只要判断数值是否相等就可以了。判断一个对象,就存在两个概念,一个是地址值相等,另一个是对象的成员变量相等。后者的使用有些细节需要注意,下面我们就来探讨这一过程。
如下所示的例子中定义一个Map集合,集合的key使用了User对象,当然User含有自己的属性。
import java.util.HashMap;
public class CSDN {
public static void main(String[] args) {
HashMap<User,String> maps = new HashMap<User,String>();
maps.put(new User("Jack","123"),"Jack");
System.out.println(maps.get(new User("Jack","123")));
}
}
class User {
public User(String name,String password){
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private String name;
private String password;
}
//out
null
这个应该很容易知道,从集合中获取该User对象不是同一个对象User。在一个上下文中,只有当使用原对象来获取集合中的值时才会返回想要的值。如果需要通过判断对象的成员变量都相等作为对象的相等的条件时。这样就需要重写equals和hashCode方法了。
先只重写equals方法看一下能不能得到数据
import java.util.HashMap;
public class CSDN {
public static void main(String[] args) {
HashMap<User,String> maps = new HashMap<User,String>();
maps.put(new User("Jack","123"),"Jack");
System.out.println(maps.get(new User("Jack","123")));
}
}
class User {
public User(String name,String password){
this.name = name;
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof User))
return false;
User user = (User) o;
return this.getName().equals(user.getName())
&& this.getPassword().equals(user.getPassword());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private String name;
private String password;
}
//out
null
为什么拿不到数据呢,这个问题就要看一下Map集合是如何来判断key为同一个的条件了。
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
上面是HashMap中的通过key获取值的方法:可以看出是通过遍历并判断hashCode 和 equals同时满足时,才会返回值。因此,需要重写equals的同时重写hashCode.
import java.util.HashMap;
public class CSDN {
public static void main(String[] args) {
HashMap<User,String> maps = new HashMap<User,String>();
maps.put(new User("Jack","123"),"Jack");
System.out.println(maps.get(new User("Jack","123")));
}
}
class User {
public User(String name,String password){
this.name = name;
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof User))
return false;
User user = (User) o;
return this.getName().equals(user.getName())
&& this.getPassword().equals(user.getPassword());
}
@Override
public int hashCode(){
int result = 17;
int c = name.hashCode() + password.hashCode();
return result + c;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private String name;
private String password;
}
//out
Jack
这就是上述的结果,下面我们描述一下主要的重写HashCode的方法,参照了effective java2ed中的描述:
/**
* 1、如果把某个非零常量值,比如说17,保存在一个名result的int类型的变量中。
* 2、对于对象中每个关键域f(值equals方法中涉及的每个域),完成一下步骤.
* a.为该域计算int类型的散列码c
* 如果该域是boolean类型: 计算(f?1:0)。
* 如果该域是byte、char、short或者int类型,则计算(int)f
* 如果该域是long类型,则计算(int)(f^(f>>>32)).
* 如果该域是float类型,则计算Float。floatToIntBits(f).
* 如果该域是double类型,则计算Double。doubleToLongBits(f)。
* 如果该域是一个对象的引用,并且该类的equals方法通过递归调用equals的方式来比较这个域,则同样为这个域递归的调用hashCode.如果需要更复杂的比较,
* 则为这个域计算一个范式,然后针对这个范式调用hashCode。如果这个域为null,则返回0(或者其他的常数,但通常返回0)。
* 如果该域是一个数组,则要把每一个元素当作单独的域来处理,也就是说,递归的应用上述规则,对每个重要的元素计算一个散列码,然后根据步骤2.b中的做法把这些散列值组合起来。如果数组
* 域中的每一个元素都很重要,可以利用发行版本1.5增加的其中一个Arrays。hashCode方法。
* b.按照下面的公式,把步骤2.a中计算的得到的散列码c合并到result中:
* result = 31* result +c
* 3.返回result
*/