Java中的equals 和hashCode的理解

前几天面试,被问了一个hashCode值相等,对象是否相等;对象相等,hashCode是否相等。

突然懵逼了,因为是面试,一下紧张,按照记忆中的说的,完全打错,结果可想而知。 可见自己对这完全不理解,故重新认识一番。



Java对于eqauls方法和hashCode方法是这样规定的: 

1、如果两个对象相同,那么它们的hashCode值一定要相同;
 
2、如果两个对象的hashCode相同,它们并不一定相同(上面说的对象相同指的是用eqauls方法比较。)  

对象默认都extends Object ,先看Object对这两个方法的定义:

public boolean equals(Object obj) {  
        return (this == obj);  
} 
是不是突然懵逼了,这明明比较对象的内存地址是否相等,也就是判断是不是同一个对象。但是为什么很多情况下都会用equals()去比较内容是否一致呢?
然后打开String类的源码,会发现:

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
会发现,这种情况下,是String重写了Object类的equals(),有了自己的实现方式,所以平常我们判断字符串内容是否相等用equals()是正确的。而且不止String类重写了,Double、Float也都重写了。  

至于hashCode()且看Object类中对它的定义:  

public native int hashCode();  
可以看到这是一个本地方法,返回的对象的地址值。

而再次拿出String类中的hashCode()方法:

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
最终也是返回一个计算出的hash值,用于判断。 

Java中的集合(Collection)有两类,一类是List,再有一类是Set。 前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。  

当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。 如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。  

public class Test01 {

	public static void main(String[] args) {
		Name name1 = new Name("24");
		Name name2 = new Name("24");
		Set set = new HashSet();
		set.add(name1);
		System.out.println("********************111");
		set.add(name2);
		System.out.println("********************222");
		System.out.println(name1.equals(name2));
		System.out.println("********************333");
		System.out.println(name1.hashCode());
		System.out.println(name2.hashCode());
		System.out.println(set);
	}
	
}

class Name {
	private String id;
	public Name(String id) {
		this.id = id;
	}
	
	public String toString() {
		return this.id;
	}
	
	public boolean equals(Object obj) {
		if(obj instanceof Name) {
			Name name = (Name) obj;
			System.out.println("equals:"+name.id);
			return (id.equals(name.id));
		}
		return super.equals(obj);
	}
	
	public int hashCode() {
		Name name = (Name) this;
		System.out.println("Hash:"+name.id);
		return id.hashCode();
	}
	
}
运行结果如下:  

public class Test01 {

	public static void main(String[] args) {
		Name name1 = new Name("24");
		Name name2 = new Name("24");
		List list = new ArrayList();
		list.add(name1);
		System.out.println("********************111");
		list.add(name2);
		System.out.println("********************222");
		System.out.println(name1.equals(name2));
		System.out.println("********************333");
		System.out.println(name1.hashCode());
		System.out.println(name2.hashCode());
		System.out.println(list);
	}
	
}
运行结果如下:  

可以看出,set进行添加的时候,先去判断hashCode,而list就不必判断hashCode,它可以保存重复数据,而set判断后,如果有相同值,不进行保存。  


总结,这些在学校都是最基础的东西,可惜当初没认真听,后来也是觉得该怎么用就怎么用,也没去思考一些最基本的东西,所以,还是要多理解。




猜你喜欢

转载自blog.csdn.net/u014682894/article/details/51923949
今日推荐