java的hashcode(结合hashset讲解)

equals()跟hashcode()都可以用来比较对象。hashcode通过不同对象有不同的散列码来比较两个对象。

hashcode方法把对象放到一个对象容器进行查找,算法好坏直接影响容器的存取效率。
HashCode() is explicitly used in methods where hash functions are used, like hashTable() etc.
One should always override hashCode() when overriding equals(). Unexpected behaviour will occur if you don't do so.
HashCode() should have the same value whenever equals() returns true.

Java中的集合(Collection)有两类,一类是List,再有一类是Set。list有序,可重复;set无序不重复。

判断两个元素是否重复?

equals方法,在集合中逐一取出元素与指定查询元素对比,有则返回true;遍历完没有,则返回false。

但如果集合太大,不能遍历,则需要使用hashcode()。

hashcode()是一个函数,通过计算将每个元素直接映射到存储位置上,如果无,则存储,如果存在则比较,相同,则不存;不同,则利用冲突方案解决(或者利用另一中方法另存一个位置,或者直接做链表)。

equals()和hashcode()这两个方法都是从object类中继承过来的。
equals()方法在object类中定义如下:
public boolean equals(Object obj) {
return (this == obj);
}

Integer、 Double等类都覆盖了hashcode()方法,如String类中定义的hashcode()方法如下:

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;
}
String的API中解释: Returns a hash code for this string. The hash code for a String object is computed as
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
返回值int为位置,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。)

hashcode()最典型的应用是在hashset中,其与equal()的关系在hashset也应用的最明显。

(hashset是set类的扩展,其内部是使用HashMap 实现,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象参见:http://blog.csdn.net/longshenlmj/article/details/11907089http://alex09.iteye.com/blog/539549
     当要存储一个元素时,先根据特定hash函数计算hashcode码,再除hashset整个表的长度,就得出了位置,再看该位置上的表中是否有相同元素(利用equal函数判断相同),没有,则存;有,则不存(不重复插入相同元素)。

Java默认的散列表(hash表单元长度)的大小全部都是2的幂,初始值为16(2的4次幂)。假如这16条链表中的75%链接有数据的时候,则加载因子达到默认的0.75。则HahSet重新散列(hash),即删掉后重新开辟一个散列单元大小为32(2的5次幂)的表,并重新计算各个数据的存储位置。以此类推下去.....

举例:

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

class Name {
    private String first;
    private String last;

    public Name(String first, String last) {
        this.first = first;
        this.last = last;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o.getClass() == Name.class) {
            Name n = (Name) o;
            return n.first.equals(first) && n.last.equals(last);
        }       
        return false;
    }
}

public class hashsettestforall {
    public static void main(String[] args) {
        Set<Name> s = new HashSet<Name>();
        s.add(new Name("abc", "123"));
        System.out.println((new Name("abc", "123")).hashCode());
        System.out.println(s.hashCode());
        System.out.println(s.contains(new Name("abc", "123")));
    }
}

此段代码,一般看可能认为输出为true。但是由于Name类只重写了equals(),hashcode没有被重写,加入元素时使用的hashcode()是继承于set<-collection<-object的,所以计算的hashcode值不同,存储位置不同,则认为元素不相同,输出false。

输出结果为:

482535999
1104499981
false

看出hashcode不同,位置不同。所以一般设计hashcode的,要求hashCode方法和equals方法同时覆盖。

注意1:

        equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,hashcode()不知道是否相等。(也就是说equals()方法不相等的两个对象,hashcode()有可能相等)。而hashcode()不等,则equals()一定不等;同样hashcode()相等,equals()可能相等,也可能不等。

所以,上述代码覆盖hashcode()函数:

public int hashCode()

{      return first.hashCode();

}

用成员first计算hashcode,则可保证first相同的对象,存储位置相同。再用equals比较就知道是否相同。

这里需要注意2:

          当一个对象存进hashset集合中以后,其包含参与计算哈希值的字段就必须固定,不能被修改。否则,hashset的哈希值就不同了,这种情况下,即使使用contains方法检索该对象时,也将返回false。这会导致无法从hashset集合中删除对象,造成内存泄露。

总结如下:

(1)只有类的实例对象要被采用哈希法存储和检索时,如hashset,这个类才需要按要求覆盖hashCode方法,通常要求hashCode方法和equals方法一并被同时覆盖。

(2)equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,hashcode()不知道是否相等。(也就是说equals()方法不相等的两个对象,hashcode()有可能相等)。而hashcode()不等,则equals()一定不等;同样hashcode()相等,equals()可能相等,也可能不等。

(3) 当一个对象存进hashset集合中以后,其包含参与计算哈希值的字段就必须固定,不能被修改。否则,hashset的哈希值就不同了,这种情况下,即使使用contains方法检索该对象时,也将返回false。这会导致无法从hashset集合中删除对象,造成内存泄露。

另外,

hashmap的hashcode()如下:

public final int hashCode() {
            return (key==null   ? 0 : key.hashCode()) ^
                   (value==null ? 0 : value.hashCode());
        } 

api解释如下:

Returns the hash code value for this map entry. The hash code of a map entry e is defined to be:

     (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
     (e.getValue()==null ? 0 : e.getValue().hashCode())
 

This ensures that e1.equals(e2) implies that e1.hashCode()==e2.hashCode() for any two Entriese1 and e2, as required by the general contract of Object.hashCode.

Specified by: hashCode() inEntry, Overrides: hashCode() inObject
Returns:
the hash code value for this map entry

Open Declarationint java.lang.Object.hashCode()也有具体解释,截取部分如下:

Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided byjava.util.Hashtable.

其他省略。

原文地址:https://www.cnblogs.com/cl1024cl/p/6205602.html