分析重写equals要重写hashCode

一般来说将类放置在容器中要重写equals()方法,放置在散列中要重写hashCode()方法,如果放置在有序容器的话要重写compareTo()方法。

在HashMap中如果key是一个自定义类那么必须重写equals和hashCode。

先分析下面代码:

情景一:

1 自定义一个实体类User:(没有重写equals和hashCode)

package com.yc.test;

public class User {
	public String name;
	public int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public User() {
		super();
		// TODO Auto-generated constructor stub
	}
	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}
	

}

2 编写测试:创建map集合,以上述User实例为key:

package com.yc.test;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import junit.framework.TestCase;
public class TestProject extends TestCase {
	
	public void test2(){
		Map<User,Object> map=new HashMap<User,Object>();
		User  user1=new User("张三",18);
		User user3=new User("张三",18);
		User user2=new User("李四",20);
		map.put(user1,"这是第一个User");
		map.put(user2,"这是第二个User");
		map.put(user3,"这是第三个User");
		System.out.println("map的大小:"+map.size());
		 for(User key:map.keySet()){
			 System.out.println(map.get(key));
		 }
	}
}

3 运行结果:

map的大小:3
这是第一个User
这是第二个User
这是第三个User

情景二 :

1 自定义实体类User(重写equals和hashCode)

package com.yc.test;
public class User {
	public String name;
	public int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	public User() {
		super();
		// TODO Auto-generated constructor stub
	}
	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}
}

2 编写测试与上述(情景一2)相同:

3 运行结果:

map的大小:2
这是第三个User
这是第二个User

分析:(附上HashMap的put()方法源码)

// 将“key-value”添加到HashMap中  
    public V put(K key, V value) {  
        // 若“key为null”,则将该键值对添加到table[0]中。  
        if (key == null)  
            return putForNullKey(value);  
        // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。  
        int hash = hash(key.hashCode());  
        int i = indexFor(hash, table.length);  
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
            Object k;  
            // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
                V oldValue = e.value;  
                e.value = value;  
                e.recordAccess(this);  
                return oldValue;  
            }  
        }  
        // 若“该key”对应的键值对不存在,则将“key-value”添加到table中  
        modCount++;
		//将key-value添加到table[i]处
        addEntry(hash, key, value, i);  
        return null;  
    }  

向map中添加值的时候会首先判断key是否为空,不为空则计算出hashCode和hash,再计算key的索引位置,若key对应位置不存在则添加,若对应key存在则覆盖value值。在情景一中没有重写equals和hashCode,当我们以没有重写equals和hashCode自定义的类为key时,会默认Object类的hashCode()和equals();比较的是对象的地址。这好比本案例的User类,user1和user3看来好像是相等的,但是其实会发现默认比较的是对象存储地址,所以user1!=user3,同理user1.equals(user3)也是错误的。

如果重写equals,你会发现user1.equals(user3)返回true,但是map集合的结果还是和情景一相同。因为hashCode()返回值不一样,即哈希值不一样,认为该键值对不存在,所以还是会重新添加键值对,不会覆盖原来value。

在情景二中重写了equals()和hashCode(),所以key值相等,user3的值会覆盖user1的值。

猜你喜欢

转载自blog.csdn.net/qq_40693828/article/details/81812142