Java hashCode 和 equals

  当向Set集合中插入对象时,如何判别在集合中是否已经存在该对象。如果采用equals方法对元素逐一进行比较,这样的做法较为耗时。可以先判断hashcode值,HashMap中用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去;如果存在该值, 就调用它的equals方法与新元素进行比较,相同则不存,不同则散列到其他位置。这样当数据量大时就减少了equals函数的调用,极大地提高了效率。

1. Equals

Object类:直接比较两个对象的地址

public boolean equals(Object obj) {  
    return (this == obj);  
}  

String类:比较对象的内容,Math、Integer、Double等类中的equals函数也是进行内容的比较

public boolean equals(Object anObject) {  
    if (this == anObject) {  
        return true;  
    }  
    if (anObject instanceof String) {  
        String anotherString = (String)anObject;  
        int n = count;  
        if (n == anotherString.count) {  
            char v1[] = value;  
            char v2[] = anotherString.value;  
            int i = offset;  
            int j = anotherString.offset;  
            while (n– != 0) {  
                if (v1[i++] != v2[j++])  
                    return false;  
            }  
            return true;  
        }  
    }  
    return false;  
}

2. Hashcode

  hashcode对于list集合没有什么意义,但对HashMap、HashSet、HashTable的存取有重要作用。在Java中hashCode方法的主要作用是为了配合基于散列的集合一起正常运行。hashcode的计算是根据对象的属性进行散列的,过多属性参与散列会降低集合的存取效率,太少则容易发生散列冲突, 从而影响hash列表的性能。

Object类:默认情况下是根据存储地址进行映射

public native int hashCode();

String类:

public int hashCode() {  
    int h = hash;  
    if (h == 0) {  
        int off = offset;  
        char val[] = value;  
        int len = count;  
  
        for (int i = 0; i < len; i++) {  
            h = 31 * h + val[off++];  
        }  
        hash = h;  
    }  
    return h;  
}

3. hashcode和equals函数的关系:

  1)  如果两个对象相同(equals比较相等),那么它们的hashCode值一定要相同。

    这样就不会导致两个相同的对象因为hashCode值不一样而同时存入HashMap, 而HashMap不允许存放重复元素

  2) 如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是用equals方法比较。

  3) 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个整数值。

· 在重写equals方法的同时,必须重写hashCode方法

package com.sa.io;

import java.util.HashMap;

class People {
    private String name;
    private int age;
    
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public boolean equals(Object obj) {
        return name.equals(((People)obj).name) && age == ((People)obj).age;
    }
}

public class HashCodeTest {
    public static void main(String[] args) {
        HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
        hashMap.put(new People("kelly", 18), 1);
        System.out.println(hashMap.get(new People("kelly", 18)));     
        // 返回值为null, 因为People只重写了equals函数没有重写hashcode函数,
        // 默认情况下,hashCode方法是将对象的存储地址进行映射。这里生成了两个对象,存储地址不一样,因此查找不到该对象
    }
}

· hashCode函数依赖的字段变化时,hashCode值也发生变化

class People {
    private String name;
    private int age;
    
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public int hashCode() {
        return name.hashCode() * 27 + age;
    }
    
    @Override
    public boolean equals(Object obj) {
        return name.equals(((People)obj).name) && age == ((People)obj).age;
    }
}

public class HashCodeTest {
    public static void main(String[] args) {
        HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
        People people = new People("kelly", 18);
        hashMap.put(people, 1);
        people.setAge(23);
        System.out.println(hashMap.get(people));     
     // 返回值为null, 因为age是hashcode函数依赖的属性,该值发生变化后hashcode值也改变
     // 如果没有重写hashcode函数,则equals返回true, 然而结果却返回1。因为get方法先用==判断对象是否相同,然后才调用equals
} }

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)))   // hash -> (key) ==  -> equals
                return e.value;
        }
        return null;
}
原文地址:https://www.cnblogs.com/anxiao/p/6733955.html