Java的Object.object hashcode方法的返回值到底是不是对象内存地址

博客访问: 118295
博文数量: 93
博客积分: 3090
博客等级: 中校
技术积分: 770
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Java
关于Object的equals()及hashCode()
JavaObjectequals()hashCode()Object
1.equals()
lnullxx.equals(x)true
lnullxyy.equals(x)truex.equals(y)true
lnullxyzx.equals(y)truey.equals(z)truex.equals(z)true
lnullxyx.equals(y)truefalse
lnullxx.equals(null)false
ObjectObjecttruereturn x ==
(override)hasCode()hash code
2.hashCode()
lJavaequals()hasCode()
lequals()hasCode()int
lequals()falsehashCode()intinthashtables
hasCode()int(intJava)
lhashCode()equals()
lequals()hashCode()equals()hashCode()Integerinthashtable
lhashCode()equals()equals()truehashCode()intequals()falseint
3hasCode()
hashCode()StringStringint
if (strA.equals(strB)) {
& return 1;
StringinthashCode()hasCode()hasCode()hashCode()
jakarta-commonslangEqualsBuilderHashCodeBuilderequals()hashCode()
EqualsBuilder
&public boolean equals(Object obj) {
&& if (obj instanceof MyClass == false) {
&& if (this == obj) {
&& MyClass rhs = (MyClass)
&& return new EqualsBuilder()
&&&&&&&&&&&&&&&& .appendSuper(super.equals(obj))
&&&&&&&&&&&&&&&& .append(field1, rhs.field1)
&&&&&&&&&&&&&&&& .append(field2, rhs.field2)
&&&&&&&&&&&&&&&& .append(field3, rhs.field3)
&&&&&&&&&&&&&&&& .isEquals();
EqualsBulder.append()
&public boolean equals(Object obj) {
&& return EqualsBuilder.reflectionEquals(this, obj);
HashCodeBuilder
&public class Person {
&& public int hashCode() {
&&&& // you pick a hard-coded, randomly chosen, non-zero, odd number
&&&& // ideally different for each class
&&&& return new HashCodeBuilder(17, 37).
&&&&&& append(name).
&&&&&& append(age).
& &&&&&append(smoker).
&&&&&& toHashCode();
name, age, smokerequals()HashCodeBuilder
&public int hashCode() {
&& return HashCodeBuilder.reflectionHashCode(this);
阅读(945) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。本帖子已过去太久远了,不再提供回复功能。java(71)
在java中,如果一个对象未重写toString()方法,那么它将会调用父类的toString(),如果父类也没有重写这个方法,那么就迭代往上调用,直到Object的toString()方法。
我们在打印这个toStirng()方法的时候,会出现XXXX@e29820字样,那么@后面的值到底是什么呢,它是对象所在的内存地址么?下面我们来证明:
public class ObjectToStringTest {
& & &* 默认值。
& & private static final int SIZE = 10000;
& & public static void main(String[] args) {
& & & & // 创建列表存放对象
& & & & List&Object& list = new ArrayList&Object&();
& & & & int existNumber = 0;
& & & & // 新建SIZE个对象,如果toStirng代表的是内存地址,地址是不会重复的,
& & & & // 那么list中应该不会存在重复的元素。
& & & & // list的大小应该为SIZE
& & & & for (int i = 0; i & SIZE; i++) {
& & & & & & Object obj = new Object();
& & & & & & if (list.contains(obj.toString())) {
& & & & & & & & System.out.println(&对象:& + obj.toString() + &已存在!&);
& & & & & & & & existNumber++;
& & & & & & } else
& & & & & & & & list.add(obj.toString());
& & & & System.out.println(&列表List的大小:& + list.size());
& & & & System.out.println(&重复元素的个数:& + existNumber);
& & & & System.out.println(&===============华丽的分割线===============&);
& & & & // 清空list
& & & & list.clear();
& & & & existNumber = 0;
& & & & // 新建一个对象的时候,变量名是对这个对象的应用(相当于对象的&地址&)
& & & & // 利用这个原理,我们再测试
& & & & for (int i = 0; i & SIZE; i++) {
& & & & & & Object obj = new Object();
& & & & & & if (list.contains(obj)) {
& & & & & & & & System.out.println(&对象:& + obj + &已存在!&);
& & & & & & & & existNumber++;
& & & & & & } else
& & & & & & & & list.add(obj.toString());
& & & & System.out.println(&列表List的大小:& + list.size());
& & & & System.out.println(&重复元素的个数:& + existNumber);
运行结果如下:
对象:java.lang.Object@922804已存在!
对象:java.lang.Object@e29820已存在!
列表List的大小:9998
重复元素的个数:2
\===============华丽的分割线===============
列表List的大小:10000
重复元素的个数:0
查看Object源代码:
public String toString() { &
& & return getClass().getName() + &@& + Integer.toHexString(hashCode()); &
原来返回的是hashCode,而hashCode算法可能出现hash冲突,所以上面的重复情况了。
PS:如果没有出现重复情况,可以增大常量SIZE。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:171278次
积分:2838
积分:2838
排名:第11301名
原创:64篇
转载:77篇
译文:18篇
评论:34条
(1)(1)(1)(2)(1)(2)(4)(2)(4)(4)(1)(1)(1)(1)(8)(125)(1)Java的Object.hashCode()的返回值毕竟是不是对象内存地址 - 编程当前位置:& &&&Java的Object.hashCode()的返回值毕竟是不是对象内Java的Object.hashCode()的返回值毕竟是不是对象内存地址&&网友分享于:&&浏览:0次Java的Object.hashCode()的返回值到底是不是对象内存地址?刚学Java的时候我也有过这种怀疑,但一直没有验证;最近在OSCHINA上看到有人在回答问题时也这么说,于是萌生了一探究竟的想法——java.lang.Object.hashCode()的返回值到底是不是对象内存地址?
(顺带回顾一下JNI)
hashCode契约
说到这个问题,大家的第一反应一定和我一样——去查Object.hashCode的源码,但翻开源码,看到的却是这样的(Oracle JDK 8):
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
java.util.HashMap}.
* The general contract of
hashCode} is:
* &li&Whenever it is invoked on the same object more than once during
an execution of a Java application, the
hashCode} method
must consistently return the same integer, provided no information
equals} comparisons on the object is modified.
This integer need not remain consistent from one execution of an
application to another execution of the same application.
* &li&If two objects are equal according to the
equals(Object)}
method, then calling the
hashCode} method on each of
the two objects must produce the same integer result.
* &li&It is &em&not&/em& required that if two objects are unequal
according to the
java.lang.Object#equals(java.lang.Object)}
method, then calling the
hashCode} method on each of the
two objects must produce distinct integer results.
However, the
programmer should be aware that producing distinct integer results
for unequal objects may improve the performance of hash tables.
* As much as is reasonably practical, the hashCode method defined by
Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java& programming language.)
a hash code value for this object.
java.lang.Object#equals(java.lang.Object)
java.lang.System#identityHashCode
public native int hashCode();
Object.hashCode是一个native方法,看不到源码(Java代码,Oracle的JDK是看不到的,OpenJDK或其他开源JRE是可以找到对应的C/C++代码)。
上面这段注释指出了Object.hashCode()在JRE(Java Runtime Library)中应该遵循的一些契约(contract):
(PS:所谓契约当然是大家一致达成的,各个JVM厂商都会遵循)
一致性(consistent),在程序的一次执行过程中,对同一个对象必须一致地返回同一个整数。
如果两个对象通过equals(Object)比较,结果相等,那么对这两个对象分别调用hashCode方法应该产生相同的整数结果。(PS:这里equals和hashCode说的都是Object类的)
如果两个对象通过java.lang.Object.equals(java.lang.Ojbect)比较,结果不相等,不必保证对这两个对象分别调用hashCode也返回两个不相同的整数。
实际上java.lang包里面的类,都是JRE必须的,属于运行时库(Runtime Library),这也是为什么很多JRE下该类的class文件被打包到rt.jar中的原因(应该是Runtime的简写)。
而这些运行时库一般都是跟JDK/JRE一起发布的;所以,对于不同的JRE环境,问题的答案未必相同。
考虑到具体JVM厂商实现的Object.hashCode相对较复杂,下面先通过另一思路对开头提出的问题进行探究。
最后我们再找一些开源JRE的Object.hashCode的具体实现作简要分析。
Java中如何获得对象内存地址?
看不到Object.hashCode的源码,反过来,我们可以得到对象的内存地址和Object.hashCode比较,也能得出结论。
要验证这个问题自然需要一种得到对象内存地址的方法,但Java本身并没有提供类似的方法;这也是我在初学Java时没有验证这个问题的原因。
“内存地址”在Java里得不到,但在C/C++中却很容易得到。于是,我们想到——通过JNI让Java代码调用一段C/C++代码来得到对象内存地址。
这里可能需要考虑的还有一点——用Java什么类型能放得下C/C++的指针?
在64位机器上,C/C++的指针是8字节;32位是4字节。
嗯(⊙_⊙)~ 不管怎样Java的long都是8字节,足矣~
Think in Java——接口和测试
假设我们已经有了一个NativeUtils.java:
class NativeUtils {
public native static long getNativePointer(Object o);
并且已经实现了getNativePointer方法。
那么验证开头提出的问题就变得异常简单了:
class TestNativeUtils {
public static void main(String args[]) {
Object o = new Object();
long nptr = NativeUtils.getNativePointer(o);
long hash = o.hashCode();
System.out.println(String.format("hash: %x, nptr: %x", hash, nptr));
Think in C++——实现native方法
好了说干就干,现在就差那个native的getNativePointer了。
用javah生成对应的.h文件:
$ javah NativeUtils
该命令执行后生成了NativeUtils.h:
#include &jni.h&
#ifndef _Included_NativeUtils
#define _Included_NativeUtils
#ifdef __cplusplus
extern "C" {
JNIEXPORT jlong JNICALL Java_NativeUtils_getNativePointer
(JNIEnv *, jclass, jobject);
#ifdef __cplusplus
接着实现这个Java_NativeUtils_getNativePointer,文件命名为NativeUtils.cc:
#include "NativeUtils.h"
JNIEXPORT jlong JNICALL
Java_NativeUtils_getNativePointer(JNIEnv *env, jclass clazz, jobject o)
return reinterpret_cast&jlong&(o);
编译为动态库:
$ g++ -shared -o libnative-utils.so NativeUtils.cc
可能因为找不到jni.h,报错:
NativeUtils.h:2:17: fatal error: jni.h: No such file or directory
在JDK安装目录下查找jni.h:
:/usr/lib/jvm/java-7-openjdk-amd64$ find . -name jni.h
./include/jni.h
知道jni.h路径后,用-I选项加到编译命令上再次编译:
$ g++ -shared -I/usr/lib/jvm/java-7-openjdk-amd64/include/ -o libnative-utils.so NativeUtils.cc
OK, 编译成功,生成了 libnative-utils.so 文件。
Run in shell——在shell环境运行
下面就让TestNativeUtils在shell环境执行起来。
首先,编译NativeUtils和TestNativeUtils:
$ javac NativeUtils.java
$ javac TestNativeUtils.java
分别生成了NativeUtils.class和TestNativeUtils.class
好了,就差临门一脚了——执行class文件:
$ java TestNativeUtils
居然出错了:
Exception in thread "main" java.lang.UnsatisfiedLinkError: NativeUtils.getNativePointer(Ljava/lang/Object
at NativeUtils.getNativePointer(Native Method)
at TestNativeUtils.main(TestNativeUtils.java:5)
加载动态库到我们的程序中
到目前为止,我们的Java代码并没有实现NativeUtils.getNativePointer;所以,会有上面的错误。
必须在调用NativeUtils.getNativePointer前,将我们编译好的动态库加载上。可以用static代码块,以保证在调用前完成加载;修改后的NativeUtils:
class NativeUtils {
System.loadLibrary("native-utils");
public native static long getNativePointer(Object o);
让JVM能找到动态库
再次编译、执行:
$ javac NativeUtils.java
$ java TestNativeUtils
又有错误,但已经和刚才不一样了:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no native-utils in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886)
at java.lang.Runtime.loadLibrary0(Runtime.java:849)
at java.lang.System.loadLibrary(System.java:1088)
at NativeUtils.&clinit&(NativeUtils.java:3)
at TestNativeUtils.main(TestNativeUtils.java:5)
这次错误是:没有在java.library.path中找到native-utils,可以在javac命令-D参数上将当期目录加到java.library.path上,让其能够找到native-utils:
$ java -Djava.library.path=. TestNativeUtils
hash: 4f5f1ace, nptr: 7f223a5fb958
All in one —— Makefile
上面的多个命令可以写到一个Makefile里,可以实现“一键执行”:
test: runtest
all: libnative-utils.so
JNI_INCLUDE=/usr/lib/jvm/java-7-openjdk-amd64/include
NativeUtils.class: NativeUtils.java
javac NativeUtils.java
TestNativeUtils.class: TestNativeUtils.java
javac TestNativeUtils.java
NativeUtils.h: NativeUtils.java
javah -jni NativeUtils
libnative-utils.so: NativeUtils.cc NativeUtils.h
g++ -shared -I${JNI_INCLUDE} -o libnative-utils.so NativeUtils.cc
runtest: TestNativeUtils.class libnative-utils.so
@echo "run test:"
java -Djava.library.path=. TestNativeUtils
rm -v *.class *.so *.h
几个JRE的具体实现
说道开源的Java运行环境,首先能想到的自然是OpenJDK和Android,下面分别简要分析。
hashCode on Android
Android的Object.hashCode和Oracle JDK的略有不同:
private transient int shadow$_monitor_;
public int hashCode() {
int lockWord = shadow$_monitor_;
final int lockWordMask = 0xC0000000;
final int lockWordStateHash = 0x;
if ((lockWord & lockWordMask) == lockWordStateHash) {
return lockWord & ~lockWordM
return System.identityHashCode(this);
(PS:估计shadow$_monitor_应该是hash值的一个cache,第一次需要计算一下,以后都不用计算了)
第一次执行时,shadow$_monitor_的值为0,将会调用System.identityHashCode:
public static native int identityHashCode(Object anObject);
而它是一个native的方法,对应代码(java_lang_System.cc):
static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
if (UNLIKELY(javaObject == nullptr)) {
ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode&mirror::Object*&(javaObject);
return static_cast&jint&(o-&IdentityHashCode());
很显然,这里的关键在于mirror::Object::IdentityHashCode:
int32_t Object::IdentityHashCode() const {
mirror::Object* current_this = const_cast&mirror::Object*&(this);
while (true) {
LockWord lw = current_this-&GetLockWord(false);
switch (lw.GetState()) {
case LockWord::kUnlocked: {
LockWord hash_word(LockWord::FromHashCode(GenerateIdentityHashCode()));
DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode);
if (const_cast&Object*&(this)-&CasLockWordWeakRelaxed(lw, hash_word)) {
return hash_word.GetHashCode();
case LockWord::kThinLocked: {
Thread* self = Thread::Current();
StackHandleScope&1& hs(self);
Handle&mirror::Object& h_this(hs.NewHandle(current_this));
Monitor::InflateThinLocked(self, h_this, lw, GenerateIdentityHashCode());
current_this = h_this.Get();
case LockWord::kFatLocked: {
Monitor* monitor = lw.FatLockMonitor();
DCHECK(monitor != nullptr);
return monitor-&GetHashCode();
case LockWord::kHashCode: {
return lw.GetHashCode();
default: {
LOG(FATAL) && "Invalid state during hashcode " && lw.GetState();
LOG(FATAL) && "Unreachable";
这段代码可以看出——ART的Object.hashCode确实是有cache的,对于同一个Ojbect,第一次调用Object.hashCode将会执行实际的计算并记入cache,以后直接从cache中取出。
真正计算hashcode的是GenerateIdentityHashCode:
int32_t Object::GenerateIdentityHashCode() {
static AtomicInteger seed( + std::time(nullptr));
int32_t expected_value, new_
expected_value = static_cast&uint32_t&(seed.LoadRelaxed());
new_value = expected_value *
} while ((expected_value & LockWord::kHashMask) == 0 ||
!pareExchangeWeakRelaxed(expected_value, new_value));
return expected_value & LockWord::kHashM
从GenerateIdentityHashCode可以看出,ART的Object.hashCode的返回值和对象的地址并没有直接的关系。
hashCode on OpenJDK
OpenJDK项目首页:openjdk.java.net
TODO: OpenJDK上的hashCode具体实现和简要分析
static JNINativeMethod methods[] = {
{"hashCode",
(void *)&JVM_IHashCode},
(void *)&JVM_MonitorWait},
{"notify",
(void *)&JVM_MonitorNotify},
{"notifyAll",
(void *)&JVM_MonitorNotifyAll},
"()Ljava/lang/O",
(void *)&JVM_Clone},
JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
(*env)-&RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return (*env)-&GetObjectClass(env, this);
这段代码指出了Object.hashCode对应的C函数为JVM_IHashCode,下面需要找到JVM_IHashCode的代码jvm.cpp:
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
JVMWrapper("JVM_IHashCode");
return handle == NULL ? 0 : ObjectSynchronizer (THREAD, JNIHandles(handle)) ;
这里只是一个包装,实际计算hashCode的是ObjectSynchronizer::FastHashCode,位于synchronizer.cpp:
intptr_t ObjectSynchronizer (Thread * Self, oop obj) {
if (UseBiasedLocking) {
if (obj-&mark()-&has_bias_pattern()) {
Handle hobj (Self, obj) ;
assert (Universe() ||
!SafepointSynchronize(),
"biases should not be seen by VM thread here");
BiasedLocking(hobj, false, JavaThread());
obj = hobj() ;
assert(!obj-&mark()-&has_bias_pattern(), "biases should be revoked by now");
assert (Universe() ||
!SafepointSynchronize(), "invariant") ;
assert (Universe() ||
Self-&is_Java_thread() , "invariant") ;
assert (Universe() ||
((JavaThread *)Self)-&thread_state() != _thread_blocked, "invariant") ;
ObjectMonitor* monitor = NULL;
markOop temp,
markOop mark = ReadStableMark (obj);
assert (!mark-&has_bias_pattern(), "invariant") ;
if (mark-&is_neutral()) {
hash = mark-&hash();
if (hash) {
hash = get_next_hash(Self, obj);
temp = mark-&copy_set_hash(hash);
test = (markOop) Atomic(temp, obj-&mark_addr(), mark);
if (test == mark) {
} else if (mark-&has_monitor()) {
monitor = mark-&monitor();
temp = monitor-&header();
assert (temp-&is_neutral(), "invariant") ;
hash = temp-&hash();
if (hash) {
} else if (Self-&is_lock_owned((address)mark-&locker())) {
temp = mark-&displaced_mark_helper();
assert (temp-&is_neutral(), "invariant") ;
hash = temp-&hash();
if (hash) {
monitor = ObjectSynchronizer(Self, obj);
mark = monitor-&header();
assert (mark-&is_neutral(), "invariant") ;
hash = mark-&hash();
if (hash == 0) {
hash = get_next_hash(Self, obj);
temp = mark-&copy_set_hash(hash);
assert (temp-&is_neutral(), "invariant") ;
test = (markOop) Atomic(temp, monitor, mark);
if (test != mark) {
hash = test-&hash();
assert (test-&is_neutral(), "invariant") ;
assert (hash != 0, "Trivial unexpected object/monitor header usage.");
又是假牙,实际计算hashCode的是get_next_hash,代码和ObjectSynchronizer::FastHashCode相邻:
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
value = os() ;
if (hashCode == 1) {
intptr_t addrBits = intptr_t(obj) && 3 ;
value = addrBits ^ (addrBits && 5) ^ GVars.stwR
if (hashCode == 2) {
value = 1 ;
if (hashCode == 3) {
value = ++GVars.hcS
if (hashCode == 4) {
value = intptr_t(obj) ;
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 =
value &= markOopD
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc, "invariant") ;
TEVENT (hashCode: GENERATE) ;
这段代码可以看出OpenJDK一共实现了5中不同的计算hash值的方法,通过
这段代码中hashCode进行切换。其中hashCode == 4的是直接使用地址的(前面的实验说明OpenJDK默认情况下并没有使用这种方式,或许可以通过运行/编译时参数进行选择)。
前面通过JNI验证已经能够得到很显然的结论,hashCode返回的并不一定是对象的(虚拟)内存地址,具体取决于运行时库和JVM的具体实现。
我的运行环境:
Ubuntu 12.04 64bit Desktop |
java version “1.7.0_55”
OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1ubuntu1~0.12.04.2) \
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 1234567891011 Copyright & &&版权所有}

我要回帖

更多关于 object的hashcode方法 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信