Effective java: 覆盖equals时总要覆盖hashCode 的探究

在Effective Java中的第九条说:覆盖equals总要覆盖hashCode。
“一个很常见的错误根源在于没有覆盖hashCode方法,在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。”
以下约定内容摘自Object规范[JavaSE6]:
1. 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时, 必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2. 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
3. 如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
显然,如果没有覆盖hashCode,就会违反第二条关键约定。
以上为理论部分,以下为实际实践的尝试,显示hashCode的作用:
首先生成一个简单的Box类:
public final class Box {
	private int length;
	private int width;
	private int heigth;
	public Box(){
		
	}
	public Box(int length, int width, int heigth){
		this.length = length;
		this.width = width;
		this.heigth = heigth;
	}
	public int getLength() {
		return length;
	}
	public void setLength(int length) {
		this.length = length;
	}
	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	public int getHeigth() {
		return heigth;
	}
	public void setHeigth(int heigth) {
		this.heigth = heigth;
	}
	
	@Override
	//重写equals方法。
	public boolean equals(Object o){
		if(o == this){
			return true;
		}else if(!(o instanceof Box)){
			return false;
		}else {
			Box boxtmp = (Box) o;
			return boxtmp.heigth == this.heigth 
				&& boxtmp.width == this.width
				&& boxtmp.length == this.length;
		}
	}
	
}

类Box中重写了equals方法。
然后写一个测试类:
iimport java.util.HashMap;

public class HashCodeAndEqualTest {
	public static void main(String[] args) {
		Map<Box, Integer> map = new HashMap<Box, Integer>();
		Box a = new Box(1,2,3);
		Box b = new Box(1,2,3);
		System.out.println("a == b :"+(a == b));
		System.out.println("a .equals(b):"+a .equals(b));
		map.put(a, 1);
		System.out.println(map.get(a));
		System.out.println(map.get(b));
		
	}
	
}

输出结果:
引用

a == b :false
a .equals(b):true
1
null

既然a.equals(b)都已经为true了,那为什么map.get(b)的返回值为null呢?map.get(b)的预期值应该和map.get(a)相等的,下面再在Box类中重写一个父类方法:hashCode():
@Override
public int hashCode(){
	int result = 0;
	result = result * 31 + length;
	result = result * 31 + heigth;
	result = result * 31 + width;
	return result;
}

然后再运行测试类,结果如下:
a == b :false
a .equals(b):true
1
1

为什么这次map.get(b) 和map.get(a)中的值相等了呢?这是因为重写了Box类中的hashCode方法,符合了第二天规定, 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
这样对象a和对象b的hash值是相等的。通俗一点儿讲, 如果a.hashCode () = b.hashCode()而且 a.euqals(b) (或 a==b) , 那么根据HashMap的算法,二者在同一个位置的,即HashMap认为只有符合以上条件,才认为a,和b是为同一key。
再深一点儿,可以参考HashMap中的get和put方法的实现,便可一探究竟。
HashMap中的get方法:
    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中的put方法:
    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        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;
           if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }


if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
这一句已经大致解释了刚才的情况,更深一点儿可以探究一下hash算法的实现,挺有意思,不过时间有限,就先写到这里了~~

你可能感兴趣的:(java,HashCode,effective)