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 ...
随机推荐
- Spring Data 整合 ElasticSearch搜索服务器
一.基于 maven 导入坐标(pom.xml文件) <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi ...
- 洛谷教主花园dp
洛谷-教主的花园-动态规划 题目描述 教主有着一个环形的花园,他想在花园周围均匀地种上n棵树,但是教主花园的土壤很特别,每个位置适合种的树都不一样,一些树可能会因为不适合这个位置的土壤而损失观赏价 ...
- javascript 命名空间与运用(前端基础系列)
所谓代码,当你随便命名一个变量:var name = "ukerxi"; 就是一句代码:但当你的代码写出来后,对于后续维护及阅读的人,就可以看出代码是否,易读,易理解:优雅的代码总 ...
- Cs Round#54 E Late Edges
题意:给定一个无向图,你从结点1开始走,每经过一条边需要1的时间,每条边都有一个开放时间,只有当目前所用的时间大于等于开放时间时,这条边才可以被经过.每一单位时间你都必须经过一条边,问最快什么时候可以 ...
- echarts分组插件echarts.group代码分享
前言 echarts是百度出品的一款很棒的前端图表控件,被评为"百度少有的良心产品".可以实现散点图.折线图.柱状图.地图.饼图.雷达图.K线图等等几十种常用.不常用的图表,效果酷 ...
- Linux下实现CAD数据的导出
近期公司项目涉及到CAD的导出,而且部署服务器申请不到Windows下的,所以技术上的解决方案就是寻求如何在Linux下实现CAD数据的导出. 于是百度了一下,找了几个相关库和软件. 1.dxflib ...
- hdu1181 变形课(vector容器+dfs)
变形课 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Total Submi ...
- 体验CSDN-Markdown
文件夹 文件夹 文本格式化练习 一号标题 1一号标题 二号标题 1 11 2 列表的应用 链接 图片 脚注 表格 序列图 流程图 文本格式化练习: 斜体 斜体的文字 使用鼠标,变成斜体文字 使用键盘C ...
- Effective Java 第三版——7. 消除过期的对象引用
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- iOS设计模式之懒加载
一.为什么要懒加载? 答: iPhone设备内存有限,如果在程序在启动后就一次性加载将来会用到的所有资源,那么久可能会耗尽iOS设备的内存.这些资源例如大量的数据,图片,音频,过多的控件等. 二.懒加 ...