hashCode方法与equals方法

1,分析为什么要重写hashCode方法和equals方法:

//学生类
class Student{
	String id;

//这里为了方便说明hashCode的重写,定义一个全局变量和构造方法
	public Student(String id) {
	
	}
}

//test类
public class Test {
public static void main(String[] args) {
	
	HashSet<Student> set=new HashSet<>();
	
	set.add(new Student("1"));
	set.add(new Student("1"));
	System.out.println(set.size());
	
	}
}

//上述代码输出:2

//理解调用add方法过程:add方法-->put方法--->putVal方法(-->1,hash方法(-->hashCode方法)-->2,equals方法)
//回忆:上一篇博客中有说到对自定义类Student来说,调用hash方法后,实质是比较对象的地址,上述两个对象的地址不同,所以调用hash方法返回的int类型值不同,在执行putVal方法后会存储成功。所以此时集合中存储了两个对象,集合长度自然为2.

//猜想:我们看到上述对象存储时,其id值是相同的,一般来讲,我们会认为id相同则为一个人,但是在上边却存入两个,那怎么实现按照id值来存储学生,相同的id只存储一个呢?下边我们详细分析

//首先调用putVal方法时,先执行了hash方法,在执行hash方法过程中会调用hashCode方法。 

String类中的hashCode方法:相同的value(即key)会有相同的返回值。

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;
}
//返回一个int类型的值,传入putVal方法,

HashMap中的putVal方法:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)		//由上篇博客可知此时获得一个存储位置下标。根据我们的猜想,如果相同的id,调用hash方法后返回的值相同,则会是同一个下标,第一次如果存入对象的话,第二次相同id的对象将不会存入了。怎样让相同的id返回相同的值?重写比较规则,即重写hashCode方法(用id调用)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))	//第二次存储相同的id时会执行此行代码,怎么样实现不存储此时的id?由下述代码知,e != null时,执行下一个if语句,直接关掉方法,不存储。e的值什么时候非null?p.hash == hash是true;(key != null && key.equals(k))也返回true时。重写equals方法后(用id调用),key.equals(k)返回true,if条件成立。
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

2,重写hashCode方法和equals方法:

class Student{
	String id;
	public Student(String id) {
	
	}

	@Override
	public int hashCode() {
		return id.hashCode();
	}
//在重写了hashCode方法后,用id调用String类中的hashCode方法,相同的id会返回相同的值,重复的id获得相同的下标,只存一次。

	@Override
	public boolean equals(Object obj) {
		Student student=(Student)obj;
		return id.equals(student.id);
	}
//在重写了equals方法后,用id调用equals方法,相同的id会返回true,由前边分析将不会存储
}

如果泛型改为Object,在重写equals方法时要注意判断存入的对象是否是Object类,其子类,或其实现类,相应的重写equals的语句为

@Override
public boolean equals(Object obj) {
	if(obj instanceof Object){
		Student student=(Student)obj;
		return id.equals(student.id);
	
	}
}

猜你喜欢

转载自blog.csdn.net/YXX_decsdn/article/details/89735769