两个不同的对象具有相同的哈希码

20

Hashcode()和equals()的概念是:

1)如果根据equals()方法两个对象相等,则调用这两个对象的hashcode方法应该产生相同的hashcode值。

另一个是:

2)根据equal()方法两个对象不相等,则调用这两个对象的hashcode方法不一定要产生不同的值。

我尝试并理解了第一个,并且以下是第一个点的代码。

public class Test {
    public static void main(String[] args) {

        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        map.put(1, 11);
        map.put(4, 11);
        System.out.println(map.hashCode());
        Map<Integer, Integer> map1 = new HashMap<Integer, Integer>();
        map1.put(1, 11);
        map1.put(4, 11);
        System.out.println(map1.hashCode());
        if (map.equals(map1)) {
            System.out.println("equal ");
        }
    }
}

上面的程序为两个不同的对象提供了相同的哈希码。

有没有人能用例子解释一下,为什么根据equals()方法这两个不同的对象是不等的,但它们却有相同的哈希码。


6
请比较可能的哈希码数量与可能的“Long”或“String”数量。参考链接:http://en.wikipedia.org/wiki/Pigeonhole_principle - SLaks
可能是Java中==,equals和hashcode的示例的重复。 - cHao
(1)和(2)点的来源是什么? - Gaurav Singh
9个回答

31

2) 如果两个对象根据equal()方法是不相等的,则并不要求调用这两个对象的hashcode方法必须产生不同的值。

根据哈希函数的不同,两个不同的对象可能具有相同的哈希码。然而,相同的两个对象在被哈希时必须产生相同的结果(除非有人使用随机数实现了一个无用的哈希函数)。

例如,如果我要哈希整数,而我的哈希函数只是简单地使用(n % 10),那么数字17和数字27将产生相同的结果。这并不意味着这些数字是相同的。


8

hashCode()方法有32位可能的值。你的对象可能会有比这更多,因此你会有一些具有相同hashCode的对象,即你不能确保它们是唯一的。

在容量有限的哈希集合中,情况变得更糟。HashMap的最大容量为1 << 30或约十亿。这意味着只有30位实际使用,如果你的集合不使用16+ GB,只有1000个桶(或者技术上说是1 << 10),那么你真正拥有的只有1000个可能的桶。

注意:在HotSpot JVM上,默认的Object.hashCode()永远不会是负数,即只有31位,尽管我不确定为什么。

如果你想生成许多具有相同hashCode的对象,请查看Long。

// from Long
public int hashCode() {
    return (int)(value ^ (value >>> 32));
}

for(long i = Integer.MIN_VALUE; i < Integer.MAX_VALUE;i++) {
    Long l = (i << 32) + i;
    System.out.print(l.hashCode()+" ");
    if (i % 100 == 0)
        System.out.println();
}

这将生成40亿个hashCode为0的长整型数。

3
如果你想生成很多具有相同哈希码的对象,那么重写 "public int hashCode()" 方法以返回相同的哈希码会更简单。该方法不是 final。 - emory

8

字符串示例(以下所有字符串的哈希码为0):

public static void main(String[] args) {
    List<String> list = Arrays.asList("pollinating sandboxes",
                                      "amusement & hemophilias",
                                      "schoolworks = perversive",
                                      "electrolysissweeteners.net",
                                      "constitutionalunstableness.net",
                                      "grinnerslaphappier.org",
                                      "BLEACHINGFEMININELY.NET",
                                      "WWW.BUMRACEGOERS.ORG",
                                      "WWW.RACCOONPRUDENTIALS.NET",
                                      "Microcomputers: the unredeemed lollipop...",
                                      "Incentively, my dear, I don't tessellate a derangement.",
                                      "A person who never yodelled an apology, never preened vocalizing transsexuals.");
    for (String s : list) {
        System.out.println(s.hashCode());
    }
}

(stolen from this post).


5
如果你了解HashMap是如何实现及其用途,那么理解它就很简单。HashMap将一个大集合的值分成更小的集合(桶),以便更快地检索元素。基本上,你只需要在一个桶里搜索你的元素,而不是在整个列表中搜索。这些桶位于一个数组中,其中索引是哈希码。每个桶都包含一个元素链表,这些元素具有相同的哈希码,但不相等()。我认为在Java 8中,当桶的大小变得很大时,它们会切换到使用TreeMap。

1

实际上,这很简单,

首先我们需要知道哈希码是什么。

在Java中,哈希码只是从相关数据中派生出来的32位有符号整数。整数类型通常只是(Int Data) Mod (某个合理大质数)。

让我们对整数进行简单的哈希。
定义:

public int hash(int num){ return num % 19 ; } 

在这种情况下,19和38都将返回哈希值0。
对于字符串类型,哈希值是从单个字符和它们在字符串中的位置派生出来的,并除以一个相当大的数字。(或者,在Java中忽略32位求和中的溢出)。
考虑到可能存在任意多个字符串,并且对于一个字符串有限数量的哈希码(2^32),鸽巢原理表明至少有两个不同的字符串会产生相同的哈希码。

1

我相信这会帮助你理解......

Java对象的哈希码(HashCode)只是一个数字,它是32位有符号整数,允许对象被哈希表数据结构管理。我们知道哈希码是JVM分配给对象的唯一标识号。但实际上,哈希码并不是对象的唯一标识号。如果两个对象相等,则这两个对象应该返回相同的哈希码。因此,我们必须以这样的方式实现类的hashCode()方法,即如果两个对象相等(即通过该类的equal()方法进行比较),则这两个对象必须返回相同的哈希码。如果您重写hashCode方法,则还需要重写equal()方法。

参考:https://www.java2novice.com/java_interview_questions/hashcode/


0

0

hashCode 的目的是为了使以下公理和推论成立:

  • 如果我们知道两个对象的哈希码,且这些哈希码不匹配,则无需进一步检查对象就可以知道它们不匹配。即使是两个任意选定的不匹配对象有 10% 的概率具有相同的哈希码,测试哈希码也能让我们消除 90% 的比较。虽然不像消除 99.99% 那么大的优势,但仍然值得一试。

  • 如果我们知道一组对象中没有一个特定的哈希码,那么这组对象中就没有任何一个对象与具有该哈希码的对象匹配。如果我们将对象集合划分为哈希码为奇数和偶数的两部分,并想要查找哈希码恰好为偶数的某个元素,则无需检查奇数哈希项目的任何内容。同样,也没有必要在偶数哈希集合中查找奇数哈希项目。即使只使用两种哈希值,搜索速度也能提高近一半。如果我们将集合划分为更小的子集,搜索速度还可以进一步提高。

请注意,如果每个不同的项返回一个不同的哈希值,则hashCode()将提供最大的好处,但即使许多项具有相同的哈希值,它也可以提供实质性的好处。90%节省和99.99%节省之间的差异通常比数字所示要大得多,因此,如果可以合理轻松地将事物改进到99%、99.9%或更高水平,则应该这样做,但是在集合中拥有零个错误匹配和拥有一些错误匹配之间的区别非常微小。

-1

我的理解是hashCode是内存地址的数字表示,但它并不是实际地址。它可以更改,而不影响实际地址。因此,即使它们是完全不同的东西,也应该可以将所有对象设置为相同的hashCode。想象一下一个街区上的每个人突然都有了相同的街道地址。他们确实是不同的人,但现在都共享同一个街道地址。他们的房子没有移动,只是一个淘气的青少年把每个人都标记为“100 N. Main”。

我对Java还比较新,所以请谨慎对待我的回复。


1
这实际上是一个关于哈希码的完全错误的想法。 - Chris Cudmore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接