K:java中的hashCode和equals方法
hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals。通过翻看Object类的相关源码,发现其hashCode的实现方式如下:
public native int hashCode();
从中可以看出,hashCode的实现是一个本地方法,并且其返回了一个int型的值。很多人都认为在默认情况下,hashCode返回的就是对象的存储地址,事实上这样的看法是不全面的,确实有些JVM在实现时是直接返回对象的存储地址的,但是在大多数的时候,其只能说是与存储地址有一定的关联,下面的是HotSpot JVM中生成hash散列值的实现:
@代码来源:http://www.cnblogs.com/dolphin0520/p/3681042.html
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 = 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 = 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;
}
ps:该实现位于hotspot/src/share/vm/runtime/synchronizer.cpp文件下。
hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。Hash相关的数据结构是根据对象的相关信息(可以称为键),通过一定的运算规则将其散列映射到一个数值上的,为此,Hash相关的数据结构具有查找和插入的速度都较快的优点。Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值也称为哈希值。而需要注意的一点是在一般情况下即使两个对象的hash值相同也不能判定这两个对象的相关信息是相同的,因为hash函数一般而言都不会是一个双射函数,为此有碰撞产生的情况存在(即两个不同的对象会得到相同的hash值)。为此,在比较两个对象是否相同的时候,便需要有equals方法作为辅助了。
至于equals方法,相信学过java基础的人都会知道,在String等类中其用于判断两个对象的值是否相等,至于在Object类中,其实现如下:
public boolean equals(Object obj) {
return (this == obj);
}
从中可以发现,对于Object类来说,其equals方法判断的只是两个对象是不是同一个对象而已,当为同一个对象的时候返回true,否则返回false。而这并不能够实现判断两个对象的值是否相同的功能,通过查看String类中的equals方法的相关源码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
我们可以发现,其重写了Object类的equals方法,使得其可以对两个对象的值是否一致进行判断。
至此,我们对于hashCode和equals方法的作用有了一定的了解,hashCode方法的作用是根据对象的相关信息(对象的字段值,对象的存储地址)通过一定的运算规则获得一个int型值,该int值在一定程度上反应了对象的有关信息。而equals方法的作用是用于判断两个对象的相关的指定信息是否是一样的。在Hash相关的数据结构中通过结合使用hashCode方法以及equals方法来提高插入和查找效率。
例如,java.util.HashMap的中put方法的具体实现:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
一般而言,对于使用Hash相关的数据结构,其会先通过调用对象的hashCode方法获取该对象的hash值,缩小了查找范围之后,再通过调用equals方法进行“精细”的查找,以缩短查找时间和范围,提高查找效率。
通过以上的分析,我们需要注意到如下的几点:
- 不同的对象可能会生成相同的hash值。为此,不能通过hash值来判断两个对象是否相等,但是可以通过hash值来判断两个对象的不相等,即hash值相等的两个对象,其不一定是相等的,而hash值不相等的两个对象,其一定是不相等的。
- equals方法返回结果为ture的一定是相等的对象
由以上的这两点,我们可以推出如下的四点内容:
- 如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等
- 如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同
- 如果两个对象的hashcode值不等,则equals方法得到的结果必定为false
- 如果两个对象的hashcode值相等,则equals方法得到的结果未知。
为此,我们可以知道,在重写equals方法的时候,必须要重写了hashCode方法。
在重写equals方法和hashCode方法的时候,需要注意以下几点:
- 在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
- 如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
- 如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
------------------------------------------->摘自《Effective Java》
重写equals和hashCode方法的一个例子:
public class HashCodeTest
{
private String name="小小";
private int age=12;
@Override
public int hashCode()
{
return age*37+name.hashCode();
}
@Override
public boolean equals(Object other)
{
if(other==this)
return true;
if(other==null)
return false;
if(other instanceof HashCodeTest)
{
HashCodeTest t=(HashCodeTest) other;
return this.age==t.age&&this.name.equals(t.name);
}
return false;
}
}
注意的一点是:
在设计hashCode方法和equals方法的时候,如果对象中的数据易变,则最好在equals方法和hashCode方法中不要依赖于该字段
K:java中的hashCode和equals方法的更多相关文章
- 关于java中的hashcode和equals方法原理
关于java中的hashcode和equals方法原理 1.介绍 java编程思想和很多资料都会对自定义javabean要求必须重写hashcode和equals方法,但并没有清晰给出为何重写此两个方 ...
- java中的hashcode()和equals()
equals()和hashcode()都继承自object类. equals() equals()方法在object类中定义如下: public boolean equals(Object obj) ...
- Java中的hashCode() 和 equals()的若干问题解答
一.hashCode()的作用 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int ...
- Java中的“==操作符”和equals方法有什么区别
Java中的"=="和equals方法究竟有什么区别? 1.==操作符 "=="操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的 ...
- Java 重写 hashCode() 和 equals() 方法
1. hashCode 1.1 基本概念 hashCode 是 JDK 根据对象的地址算出来的一个 int 数字(对象的哈希码值),代表了该对象再内存中的存储位置. hashCode() 方法是超级类 ...
- 有关java中的hashCode问题
1. HashSet集合存储数据的结构(哈希表) 1.1 什么是哈希表? 哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放时的位置比较特殊,当需要把这些对象给数组中存放时,那么会 ...
- 浅谈Java中的hashcode方法以及equals方法
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...
- JAVA中的各种 哈希码(HashCode) 与 equals方法在HIBERNATE的实际应用[转载]
1.什么是哈希码(HashCode) 在Java中,哈希码代表对象的特征.例如对象 Java代码 String str1 = “aa”, str1.hashCode= 3104 String str2 ...
- Java 中正确使用 hashCode 和 equals 方法
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...
随机推荐
- thinkphp中各字母代表的发放和具体实例
hinkphp单字母函数使用指南A方法 A方法用于在内部实例化控制器,调用格式:A('[项目://][分组/]模块','控制器层名称') 最简单的用法: $User = A('User'); 复制代码 ...
- hibernate log4j
log4j.rootLogger=warn, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender. ...
- Hbuilder app开发,使用mui.ajax和服务器交互,后台获取不到值,显示null的解决方法
先上一个能用的js代码: function login() { var uname=document.getElementById("username").value.trim() ...
- 时间紧任务重---extjs的学习就这么开始吧
我们的extjs借助了一个模板引擎--artTemplate,它是一个开源的项目,不多说,给个链接吧:http://aui.github.io/artTemplate/ 直接上代码: <!DOC ...
- 最全Pycharm教程(28)——Pycharm搜索导航之搜索应用实例
1.主题 这里我们将介绍Pycharm另外一项强力的搜索导航功能.如果你希望知道某个特定的类或方法都在project中的哪些地方发挥了作用.也就是找出其全部的usages,这将是一个很巨大而繁琐的pr ...
- 在应用中更新App版本号
在应用中, 为了提高用户体验, 会提供更新版本号的功能. 那么怎样实现呢? 我写了一个简单的Demo, 说明一下, 须要注意几个细节. 使用了Retrofit和Rx处理网络请求. Github下载地址 ...
- 【日常学习】【线性DP】codevs1044 拦截导弹题解
题目描写叙述 Description 某国为了防御敌国的导弹突击,发展出一种导弹拦截系统.可是这样的导弹拦截系统有一个缺陷:尽管它的第一发炮弹可以到达随意的高度,可是以后每一发炮弹都不能高于前一发的高 ...
- Android多线程研究(2)——定时器
先来看一段代码: public static void main(String[] args) { new Timer().schedule(new TimerTask() { @Override p ...
- HTTP响应状态码含义参考
1xx:信息 100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求.101 Switching Protocols服务器转换协议:服务器将遵从客 ...
- TP3.2.3 接入支付宝
TP3.2.3 接入支付宝 项目接入支付宝支付了,在做这个给我的感觉是,方便 ,毕竟是老马的产品是吧, 话不多说 , 首先我们先找到官方的SDK ,不想去找的小伙伴复制此链接 https://doc ...