欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Effective java: 覆盖equals时总要覆盖hashCode 的探究 博客分类: Effective javajava知识 hashCodeequals方法effectiveJava 

程序员文章站 2024-02-15 08:59:58
...
在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算法的实现,挺有意思,不过时间有限,就先写到这里了~~