hashCode()是如何在Java中计算的
在java中返回hashCode()
方法的值是什么?
我读它是一个对象的内存引用…当我打印new Integer(1)
散列值是1; 对于String("a")
是97。
我很困惑:是ASCII还是什么types的值?
哈希码是一个整数值,表示被调用的对象的状态。 这就是为什么设置为1的Integer
将返回“1” Integer's
哈希码,因为Integer's
哈希码和它的值是相同的东西。 一个字符的哈希码等于它的ASCII字符码。 如果您编写自定义types,则需要负责创build一个能够最好地表示当前实例状态的hashCode
实现。
hashCode()
返回的值hashCode()
不能保证是对象的内存地址。 我不确定在Object
类中的实现,但是请记住,大多数类将重写hashCode()
,使得两个语义等价(但不是相同的实例)的实例将散列到相同的值。 如果类可以在另一个数据结构(如Set)中使用,而这个数据结构依赖于hashCode
与equals
一致,这一点尤其重要。
没有什么hashCode()
可以唯一标识一个对象的实例。 如果你想要一个基于底层指针的散列码(例如在Sun的实现中),使用System.identityHashCode()
– 这将委托给默认的hashCode
方法,而不pipe它是否被覆盖。
尽pipe如此,甚至System.identityHashCode()
也可以为多个对象返回相同的散列值。 请参阅注释以获得解释,但这里是一个示例程序,它持续生成对象,直到find两个具有相同System.identityHashCode()
。 当我运行它时,它很快find两个System.identityHashCode()
,匹配平均后,大约86,000长包装对象(和密钥的整数包装)添加到地图。
public static void main(String[] args) { Map<Integer,Long> map = new HashMap<>(); Random generator = new Random(); Collection<Integer> counts = new LinkedList<>(); Long object = generator.nextLong(); // We use the identityHashCode as the key into the map // This makes it easier to check if any other objects // have the same key. int hash = System.identityHashCode(object); while (!map.containsKey(hash)) { map.put(hash, object); object = generator.nextLong(); hash = System.identityHashCode(object); } System.out.println("Identical maps for size: " + map.size()); System.out.println("First object value: " + object); System.out.println("Second object value: " + map.get(hash)); System.out.println("First object identityHash: " + System.identityHashCode(object)); System.out.println("Second object identityHash: " + System.identityHashCode(map.get(hash))); }
示例输出:
Identical maps for size: 105822 First object value: 7446391633043190962 Second object value: -8143651927768852586 First object identityHash: 2134400190 Second object identityHash: 2134400190
如果你想知道他们是如何受到伤害的,我build议你阅读来源。 如果您正在使用IDE,您可以只使用您感兴趣的方法,并查看方法的实现方式。 如果你不能这样做,你可以谷歌的来源。
例如,Integer.hashCode()被实现为
public int hashCode() { return value; }
和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; }
我读到它是一个对象的内存引用..
大约14年前, Object.hashCode()
用于返回一个内存地址。 从此以后。
什么types的价值是
它是完全取决于你在说什么类,是否重写了Object.hashCode()。
hashCode()
方法通常用于识别一个对象。 我认为Object
实现返回的指针(不是一个真正的指针,但唯一的ID或类似的东西)的对象。 但是大多数类都会覆盖该方法。 像String
类一样。 两个string对象有不同的指针,但它们是相等的:
new String("a").hashCode() == new String("a").hashCode()
我认为hashCode()
最常见的用法是在Hashtable
, HashSet
等。
Java API对象hashCode()
编辑:(由于最近downvote和基于我阅读有关JVM参数的文章)
使用JVM参数-XX:hashCode
,可以更改hashCode的计算方式(请参阅Java专家新闻简报的第222期)。
HashCode == 0:简单地返回随机数字,与内存中find对象的位置无关。 据我所知,对于有很多处理器的系统,种子的全局读写并不是最佳的。
HashCode == 1:计算哈希代码值,不确定它们以什么值开始,但似乎相当高。
HashCode == 2:总是返回完全相同的标识哈希码1.这可以用来testing依赖于对象标识的代码。 在上面的例子中,JavaChampionTest返回Kirk的URL的原因是所有对象都返回相同的哈希码。
HashCode == 3:从零开始计算哈希码值。 它看起来不是线程安全的,所以多个线程可以使用相同的哈希码生成对象。
HashCode == 4:这似乎与创build对象的内存位置有一些关系。
HashCode> = 5:这是Java 8的默认algorithm,具有每个线程的种子。 它使用Marsaglia的xor-shiftscheme来产生伪随机数。
如果内存服务正确(检查java.lang.Object的JavaDoc),则Object.hashCode()是与实现相关的,并且将根据对象而变化(Sun JVM将该值从引用的值派生到对象)。
请注意,如果您正在实现任何非平凡的对象,并且想要将它们正确地存储在HashMap或HashSet中,则必须重写hashCode()和equals()。 hashCode()可以做任何你喜欢的事情(这是完全合法的,但是让它返回1是不理想的),但是至关重要的是,如果你的equals()方法返回true,那么hashCode()返回的值就是相等的。
混淆和对hashCode()和equals()的理解缺乏是错误的重要来源。 确保你完全熟悉了Object.hashCode()和Object.equals()的JavaDocs,并且保证花费的时间会为自己付出代价。
public static int murmur3_32(int paramInt1, char[] paramArrayOfChar, int paramInt2, int paramInt3) { /* 121 */ int i = paramInt1; /* */ /* 123 */ int j = paramInt2; /* 124 */ int k = paramInt3; /* */ /* */ int m; /* 127 */ while (k >= 2) { /* 128 */ m = paramArrayOfChar[(j++)] & 0xFFFF | paramArrayOfChar[(j++)] << '\020'; /* */ /* 130 */ k -= 2; /* */ /* 132 */ m *= -862048943; /* 133 */ m = Integer.rotateLeft(m, 15); /* 134 */ m *= 461845907; /* */ /* 136 */ i ^= m; /* 137 */ i = Integer.rotateLeft(i, 13); /* 138 */ i = i * 5 + -430675100; /* */ } /* */ /* */ /* */ /* 143 */ if (k > 0) { /* 144 */ m = paramArrayOfChar[j]; /* */ /* 146 */ m *= -862048943; /* 147 */ m = Integer.rotateLeft(m, 15); /* 148 */ m *= 461845907; /* 149 */ i ^= m; /* */ } /* */ /* */ /* */ /* 154 */ i ^= paramInt3 * 2; /* */ /* */ /* 157 */ i ^= i >>> 16; /* 158 */ i *= -2048144789; /* 159 */ i ^= i >>> 13; /* 160 */ i *= -1028477387; /* 161 */ i ^= i >>> 16; /* */ /* 163 */ return i; /* */ }
如果你真的好奇学习,那么通过Hashing.class中可用的代码;
这里第一个参数HASHING_SEED是根据下面的代码计算的
{ long nanos = System.nanoTime(); long now = System.currentTimeMillis(); int SEED_MATERIAL[] = { System.identityHashCode(String.class), System.identityHashCode(System.class), (int) (nanos >>> 32), (int) nanos, (int) (now >>> 32), (int) now, (int) (System.nanoTime() >>> 2) }; // Use murmur3 to scramble the seeding material. // Inline implementation to avoid loading classes int h1 = 0; // body for (int k1 : SEED_MATERIAL) { k1 *= 0xcc9e2d51; k1 = (k1 << 15) | (k1 >>> 17); k1 *= 0x1b873593; h1 ^= k1; h1 = (h1 << 13) | (h1 >>> 19); h1 = h1 * 5 + 0xe6546b64; } // tail (always empty, as body is always 32-bit chunks) // finalization h1 ^= SEED_MATERIAL.length * 4; // finalization mix force all bits of a hash block to avalanche h1 ^= h1 >>> 16; h1 *= 0x85ebca6b; h1 ^= h1 >>> 13; h1 *= 0xc2b2ae35; h1 ^= h1 >>> 16; HASHING_SEED = h1; }
第二个参数是String的char数组,第三个总是'0',第四个是char数组长度。
以上计算仅适用于String哈希码。
对于所有整数,其散列码将是其整数值。 对于字符(最多两个字母),它将是ASCII码。