为什么重写equals方法必须重写hashcode

直接先运行一段代码,看结果:

public class Test {

	public static void main(String[] args) {
		Map map = new HashMap<>();
		map.put(new User("11"), 11);
		map.put(new User("22"), 22);
		map.put(new User("33"), 33);
		System.out.println(map.get(new User("11")));//此时运行结果为null
	}
}

class User {
	private String id;

	public User(String id) {
		this.id = id;
	}

//	@Override
//	public int hashCode() {
//		return 31 * id + id.hashCode();
//	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null){
			return false;
		}
		if (obj instanceof User) {
			obj = (User) obj;
			return this.id.equals(((User) obj).getId());
		}
		return false;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
}

输出为null,这是为什么,要看map.get()源码:

   //方法1
   public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }   
   
   //方法2
   final Entry getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

    //方法3
    final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

通过以上源码可以看出get的时候会比较hashCode()的值,再比较equals,所以必须要二者重写。

有些程序可能运行中会存在不稳定的情况,建议从这方便考虑下。曾经有个系统不稳定,给所有实体类重写了这2个方法,问题得以解决。

hashCode()方法重写的一些原则

a.如果重写equals()方法,检查条件“两个对象通过equals()方法判断相等,那么它们的hashCode()也应该相等”是否成立,如果不成立,则重写hashCode()方法。
b.hashCode()不能太简单,否则容易造成hash冲突;
c.hashCode()不能太复杂,否则会影响性能。

但是一般来说我们不需要自己去写,这里有几种便捷的实现方式

(1) Google的Guava项目里有处理hashCode()和equals()的工具类com.google.common.base.Objects;
(2) Apache Commons也有类似的工具类EqualsBuilder和HashCodeBuilder;
(3) Java 7 也提供了工具类java.util.Objects;
(4) 常用IDE都提供hashCode()和equals()的代码生成。

你可能感兴趣的:(java)