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 ...
随机推荐
- MySQL管理员珍藏:十大必备工具盘点
作者:dongdongzzcs 第1页: [IT168 专稿]本文的作者Daniel Nichter是MySQL工具的开发者,他为MySQL管理员推荐了十款必备工具.以下是全文内容: MySQL是一套 ...
- POJ 1511 Invitation Cards 链式前向星+spfa+反向建边
Invitation Cards Time Limit: 8000MS Memory Limit: 262144K Total Submissions: 27200 Accepted: 902 ...
- eclipse和myeclipse中将项目发布到tomcat的root目录
1.在eclipse中,将项目直接部署在tomcat的root目录中,这样便可以直接ip:port访问项目: 项目右键->属性->web project settings 添加之后直接启动 ...
- SQL Server学习之路(二):主键和外键
0.目录 1.定义 1.1 什么是主键和外键 1.2 主键和外键的作用 1.3 主键.外键和索引的区别 2.主键(primary key) 2.1 通过SSMS设置主键 2.2 通过SQL语句设置主键 ...
- 计算器的单元测试dome
通过上一篇文章,Appium环境已经搭建好了,下面我们通过Python脚本来测试原生的计算器程序 --------------------------------------------------- ...
- vue.js事件,属性,以及交互
这是我学习vue的第二天,今天主要学习了如何利用vue阻止事件冒泡,阻止事件的默认行为,键盘事件以及如何添加class.style这些属性,以及如何利用vue来进行数据交互,利用百度的一个API来写一 ...
- C语言递归实现二叉树(二叉链表)的三种遍历和销毁操作(实验)
今天写的是二叉树操作的实验,这个实验有三个部分: ①建立二叉树,采用二叉链表结构 ②先序.中序.后续遍历二叉树,输出节点值 ③销毁二叉树 二叉树的节点结构定义 typedef struct BiTNo ...
- Windows下编译nginx-rtmp-module
http://nginx.org/en/docs/howto_build_on_win32.html 官网上的操作说明. 官网的方法Nginx编译方法,思路是一致的,只是有几个细节地方需要调整. 需要 ...
- 》》stroll--各种特效下拉菜单
<!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- Juel 表达式使用
JUEL 包的结构例如以下: 1.1.1. Juel maven仓库配置 眼下最新的版本号是2.2.7.使用的时候在pom.xml中加入仓库坐标就可以. <dependency> < ...