hashCode()
方法在java中返回什么值?
我读到它是对象的内存引用...当我打印new Integer(1)
的哈希值时,它是1; String("a")
为97.
我很困惑:它是ASCII还是什么类型的值?
答案 0 :(得分:45)
hashCode()
返回的值绝不保证是对象的内存地址。我不确定Object
类中的实现,但请记住,大多数类将覆盖hashCode()
,这样两个语义等效的实例(但不是同一个实例)将哈希到相同的价值。如果类可以在另一个依赖于hashCode
与equals
一致的数据结构(例如Set)中使用,则这一点尤为重要。
没有hashCode()
唯一标识对象实例,无论如何。如果你想要一个基于底层指针的哈希码(例如在Sun的实现中),使用System.identityHashCode()
- 这将委托默认的hashCode
方法,无论它是否被覆盖。
尽管如此,即使System.identityHashCode()
也可以为多个对象返回相同的哈希值。请参阅注释以获得解释,但这是一个示例程序,它连续生成对象,直到找到两个具有相同System.identityHashCode()
的对象。当我运行它时,它会在向地图添加大约86,000个Long包装器对象(以及键的整数包装器)后,很快找到两个匹配的System.identityHashCode()
。
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
答案 1 :(得分:44)
哈希码是一个整数值,表示调用它的对象的状态。这就是为什么设置为1的Integer
将返回哈希码“1”,因为Integer's
哈希码及其值是相同的。字符的哈希码等于它的ASCII字符代码。如果您编写自定义类型,则您有责任创建一个最佳表示当前实例状态的良好hashCode
实现。
答案 2 :(得分:21)
如果您想知道它们的实施方式,我建议您阅读来源。如果您使用的是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;
}
答案 3 :(得分:6)
hashCode()
方法通常用于标识对象。我认为Object
实现返回对象的指针(不是真正的指针,而是唯一的id或类似的东西)。但大多数类都会覆盖该方法。就像String
类一样。两个String对象的指针不同,但它们是相同的:
new String("a").hashCode() == new String("a").hashCode()
我认为hashCode()
最常见的用途是Hashtable
,HashSet
等。
编辑(由于最近的downvote并基于我读到的有关JVM参数的文章)
使用JVM参数-XX:hashCode
,您可以更改hashCode的计算方式(请参阅Java专家通讯的Issue 222)。
HashCode == 0:只返回与where无关的随机数 在内存中找到了对象。就我所知,全球化 对于有很多种子的系统,种子的读写不是最佳的 处理器。
HashCode == 1:计算哈希码值,不确定值是多少 他们开始,但似乎很高。
HashCode == 2:始终返回完全相同的身份哈希码1。 这可用于测试依赖于对象标识的代码。该 JavaChampionTest在上面的例子中返回Kirk的URL的原因 是所有对象都返回相同的哈希码。
HashCode == 3:从零开始计算哈希码值。它 看起来不是线程安全的,因此可以生成多个线程 具有相同哈希码的对象。
HashCode == 4:这似乎与内存位置有一些关系 在哪个对象被创建。
HashCode&gt; = 5:这是Java 8的默认算法,并且有一个 每线程种子。它使用Marsaglia的xor-shift方案来制作 伪随机数。
答案 4 :(得分:5)
我读到它是对象的内存引用..
没有。 Object.hashCode()
用于返回大约14年前的内存地址。不是从那以后。
是什么类型的价值
它究竟取决于你所说的是什么类,以及它是否覆盖了`Object.hashCode()。
答案 5 :(得分:2)
Object.hashCode(),如果内存正确服务(检查JavaDoc for java.lang.Object),是依赖于实现的,并将根据对象进行更改(Sun JVM从引用的值派生值)对象)。
请注意,如果要实现任何非平凡对象,并且想要将它们正确地存储在HashMap或HashSet中,则必须覆盖hashCode()和equals()。 hashCode()可以做任何你喜欢的事情(它完全是合法的,但是让它返回1是次优的。)但是,如果你的equals()方法返回true,那么hashCode()为两个对象返回的值是相等的,这一点至关重要。
混乱和缺乏对hashCode()和equals()的理解是错误的重要来源。确保您完全熟悉Object.hashCode()和Object.equals()的JavaDocs,并且我保证花费的时间将为自己付出代价。
答案 6 :(得分:2)
从OpenJDK来源(JDK8):
使用默认值5生成哈希码:
product(intx, hashCode, 5,
"(Unstable) select hashCode generation algorithm")
一些恒定数据和一个随机生成的数字,每个线程启动一个种子:
// thread-specific hashCode stream generator state - Marsaglia shift-xor form
_hashStateX = os::random() ;
_hashStateY = 842502087 ;
_hashStateZ = 0x8767 ; // (int)(3579807591LL & 0xffff) ;
_hashStateW = 273326509 ;
然后,此函数创建hashCode(如上所述,默认为5):
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = cast_from_oop<intptr_t>(obj) ;
} else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
因此我们可以看到至少在JDK8中,默认设置是特定于随机线程的。
答案 7 :(得分:2)
定义:String hashCode() 方法以整数形式返回字符串的哈希码值。
语法: public int hashCode()
Hashcode 使用以下公式
计算s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
where:
s is ith character in the string
n is length of the string
^ is exponential operand
示例: 例如,如果您想计算字符串“abc”的哈希码,那么我们有以下详细信息
s[] = {'a', 'b', 'c'}
n = 3
因此哈希码值将计算为:
s[0]*31^(2) + s[1]*31^1 + s[2]
= a*31^2 + b*31^1 + c*31^0
= (ASCII value of a = 97, b = 98 and c = 99)
= 97*961 + 98*31 + 99
= 93217 + 3038 + 99
= 96354
所以“abc”的哈希码值为96354
答案 8 :(得分:0)
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哈希码。
对于所有整数,其哈希码将是其整数值。 对于char(最多两个字母),它将是ASCII码。
答案 9 :(得分:0)
尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但Java™编程语言不需要此实现技术。)
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--