Preface

  Though I have used Java programme language for almost a year, I'm not familiar with a notion 'hash'. I have gotten a little knowledge about hash recently because I am reading a book of algorithm, which introduce the hash function and so on. So I follow the example in the book to write a sample hash routine. During the process, I'm baffling what is hashCode of the Java. As a consequence, I turn to the omnipotent internet. I will share my acquisition. In the following Articles, I will summarize some knowledge of hashCode in the java. In this article, I will concenterate on the topic why we need overwrite the hashCode() when we overwrite the equals()

What is the feature of equals()

  In the java, each object have its own equals function. The function is defined in its parent java.lang.Object. when we want to compare two object if they are 'the same', we prefer to use it. I think many programmers have ever used it, and must be familiar with its features.

What is hashCode()

  It is by design useful for only one thing: putting an object in a hash table. Hence the name. As we all known, we put an object into a hash table accroding the hash code. The code is a identity of a object in the hash table. when we want to achieve the object from the table, we should provide the hash code of the object. Then we can calculate the position of the object in the table. I will not introduce much knowledge about the mechanism of a hash function.

Why we should overwrite the hashCode() when we overwrite the equals()

 In the java, the hashCode() function is closely associated with HashMap and HashSet. From the source code of the two collections, we can figure out the relationship. From the put method and the get method of HashMap,

  public V put(K key, V value)
{
//if the key is null, invoke the putForNullKey function
if (key == null)
return putForNullKey(value);
//derive the key's hashCode and invoke the hash method to calculate its hash value
int hash = hash(key.hashCode());
//search the index of the hash value in the hash table
int i = indexFor(hash, table.length);
//if the Entry of the 'i' index is not null, loop the e
for (Entry<K,V> e = table[i]; e != null; e = e.next)
{
Object k;
//if find a key and its hash value is the same with the target object's hash value and the key is equals the target object,
//return the key's value
if (e.hash == hash && ((k = e.key) == key
|| key.equals(k)))
{
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//if the Entry of the 'i' index is null,
modCount++;
//add the target key and its value to the table
addEntry(hash, key, value, i);
return null;
}
  public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}

From the code above, it is obvious that when we employ a HashMap or HashSet and use their put and get, the collection will compare both of the objects's hash value .

So the key point of overwriting the hashCode() is that the same hashCode should be returned no matter when we invoke the hashCode() of a object.

If the hashCode() does not return the same hash value, the 'put' and the 'get' method will not get the same hash value, which will lead to a terrible result that we can not get the desired object. As a conquence, if we use the equals and HashMap or HashSet, we should take care of the hashCode(). That is when we overwrite the equals(), we need overwrite the hashCode().

  To explain the reason in deatil, I prefer to quote a example from a bloger named 'fhuan123' to illustrate my point.

       我们看一个简单的例子,就能更加清楚的理解上面的意思。假定我们写了一个类:Person (人),我们判断一个对象“人”是否指向同一个人,只要知道这个人的身份证号一直就可以了。

先来个没有重写Code类的hashcode()的例子吧,看看是什么效果:

 package com.fit;

 import java.util.HashMap;

 /**
* 身份证类
*
* @author ZYD
*
*/
public class Code { /**
* 身份证号码,一旦确定就不能更改
*/
private final int id; public int getId() {
return id;
} /**
* 通过构造方法确定身份证号码
*
* @param id
*/
public Code(int id) {
this.id = id;
} /**
* 重写equals()方法
*/
public boolean equals(Object o) {
// 如果地址一样,则两个对象相同
if (this == o) {
return true;
}
// 如果两个对象是同一类型,则比较其属性值是否都相同。如果都相同,则说明两个对象也相同;否则,说明这两个对象不相同。
if (o instanceof Code) {
Code co = (Code) o;
boolean b = (co.id == this.id);
return b;
}
return false;
} /**
* 重写toString()方法
*/
public String toString() {
return "【身份证】:" + id;
} /**
* 测试
* @param args
*/
public static void main(String[] args) { HashMap<Code, Person> map = new HashMap<Code, Person>(); Person p1 = new Person(new Code(10001),"张三");
Person p2 = new Person(new Code(10002),"李四"); map.put(p1.getCode(), p1);
map.put(p2.getCode(), p2); System.out.println("HashMap 中存放的人员信息:\n"+map); //张三改名为张山,身份证号不变。
Person p3 = new Person(new Code(10001),"张山");
map.put(p3.getCode(), p3); System.out.println("张三改名为张山后 HashMap 中存放的人员信息:\n"+map); //查找身份证为10001 的人员信息
System.out.println("查找身份证为:10001 的人员信息:"+map.get(new Code(10001)));
}
} /**
* 人类
* @author Administrator
*
*/
class Person { /**
* 每一个成人都有一个身份证
*/
private Code code; /**
* 姓名
*/
private String name; public Code getCode() {
return code;
} public void setCode(Code code) {
this.code = code;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Person() { } public Person(Code code, String name) {
this.code = code;
this.name = name;
} /**
* 重写equals()方法 当两个人得身份证号相同以及姓名相同时,表示这两个人是同一个人。
*/
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Person) {
Person p = (Person) o;
boolean b = this.code.equals(p.code) && this.name.equals(p.name);
return b;
}
return false;
} /**
* 重写toString()方法
*/
public String toString() {
return "【姓名】:" + name + " ";
}
}

  

运行结果:

HashMap 中存放的人员信息:
{【身份证】:10002=【姓名】:李四  ,
【身份证】:10001=【姓名】:张三  }
张三改名为张山后 HashMap 中存放的人员信息:
{【身份证】:10002=【姓名】:李四  ,
【身份证】:10001=【姓名】:张三  , 【身份证】:10001=【姓名】:张山  }
查找身份证为:10001 的人员信息:null

从上面的结果可以看出:

我们所做的更新和查找操作都失败了。失败的原因就是我们的身份证类: Code 没有覆写 hashCode()
方法。这个时候,当查找一样的身份证号码的键值对的时候,使用的是默认的对象的内存地址来进行定位。这样,后面的所有的身份证号对象

new
Code(10001) 产生的 hashCode () 值都是不一样的,所以导致操作失败。

重写Code类的hashcode(),代码上:

 package com.fit;

 import java.util.HashMap;

 /**
* 身份证类
*
* @author ZYD
*
*/
public class Code { /**
* 身份证号码,一旦确定就不能更改
*/
private final int id; public int getId() {
return id;
} /**
* 通过构造方法确定身份证号码
*
* @param id
*/
public Code(int id) {
this.id = id;
} /**
* 重写equals()方法
*/
public boolean equals(Object o) {
// 如果地址一样,则两个对象相同
if (this == o) {
return true;
}
// 如果两个对象是同一类型,则比较其属性值是否都相同。如果都相同,则说明两个对象也相同;否则,说明这两个对象不相同。
if (o instanceof Code) {
Code co = (Code) o;
boolean b = (co.id == this.id);
return b;
}
return false;
} /**
* 重写hashcode()方法,以身份证号码作为hash码。
*
* @return
*/
public int hashCode() {
return id;
} /**
* 重写toString()方法
*/
public String toString() {
return "【身份证】:" + id;
} /**
* 测试
* @param args
*/
public static void main(String[] args) { HashMap<Code, Person> map = new HashMap<Code, Person>(); Person p1 = new Person(new Code(10001),"张三");
Person p2 = new Person(new Code(10002),"李四"); map.put(p1.getCode(), p1);
map.put(p2.getCode(), p2); System.out.println("HashMap 中存放的人员信息:\n"+map); //张三改名为张山,身份证号不变。
Person p3 = new Person(new Code(10001),"张山");
map.put(p3.getCode(), p3); System.out.println("张三改名为张山后 HashMap 中存放的人员信息:\n"+map); //查找身份证为10001 的人员信息
System.out.println("查找身份证为:10001 的人员信息:"+map.get(new Code(10001)));
}
} /**
* 人类
* @author Administrator
*
*/
class Person { /**
* 每一个成人都有一个身份证
*/
private Code code; /**
* 姓名
*/
private String name; public Code getCode() {
return code;
} public void setCode(Code code) {
this.code = code;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Person() { } public Person(Code code, String name) {
this.code = code;
this.name = name;
} /**
* 重写equals()方法 当两个人得身份证号相同以及姓名相同时,表示这两个人是同一个人。
*/
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Person) {
Person p = (Person) o;
boolean b = this.code.equals(p.code) && this.name.equals(p.name);
return b;
}
return false;
} /**
* 重写toString()方法
*/
public String toString() {
return "【姓名】:" + name + " ";
}
}

运行效果:

HashMap 中存放的人员信息:
{【身份证】:10001=【姓名】:张三  , 【身份证】:10002=【姓名】:李四 
}
张三改名为张山后 HashMap 中存放的人员信息:
{【身份证】:10001=【姓名】:张山  , 【身份证】:10002=【姓名】:李四 
}
查找身份证为:10001 的人员信息:【姓名】:张山

Summary

  if we will put the target class into a colleciton of hash, such as HashMap or HashSet, we would better to overwrite the hashCode() method. As a result of overwriting , if a object equals the other, they should have the same hash value.

  In the next chapter, I will summary some rules and guidlines when overwrite a hashCode() method.

References

  Guidelines and rules for GetHashCode

  java中HashMap详解

  JAVA中重写equals()方法为什么要重写hashcode()方法说明

  重写equal 的同时为什么必须重写hashcode?

  解析Java对象的equals()和hashCode()的使用

  

Why we should overwrite the hashCode() when we overwrite the equals()的更多相关文章

  1. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

  2. hashCode之二--Java:重写equals()和hashCode()

    以下内容总结自<Effective Java>. 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用in ...

  3. 【转】Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例

    原文地址:http://www.cnblogs.com/luankun0214/p/4421770.html 感谢网友的分享,记录下来只为学习. 1.重写equals方法实例   部分代码参考http ...

  4. Java中equals()和hashCode()的关系以及重写equals()和hashCode()的重要性

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6580647.html  一:关系 如果两个对象相等(equal),它们的hashcode一定相同: 如果两个对 ...

  5. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例

    1.重写equals方法实例   部分代码参考http://blog.csdn.net/wangloveall/article/details/7899948 重写equals方法的目的是判断两个对象 ...

  6. How to implement equals() and hashCode() methods in Java[reproduced]

    Part I:equals() (javadoc) must define an equivalence relation (it must be reflexive, symmetric, and ...

  7. hashCode的作用

    在一般的应用中你不需要了解hashCode的用法,但当你用到HashMap,HashSet等集合类时要注意下hashCode.     你想通过一个object的key来拿HashMap的value, ...

  8. JAVA中用堆和栈的概念来理解equals() "=="和hashcode()

    在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...

  9. equals()和hashCode()隐式调用时的约定

    package com.hash; import java.util.HashMap; public class Apple { private String color; public Apple( ...

随机推荐

  1. Day 5 字典的操作

    1. 例子 ,务必理解 dic = { 'name':'金鑫', 'name_list':[1,2,3,'李杰'], 1:{ 'python10':['小黑','萌哥'], '老男孩':{'name' ...

  2. python小数的进位与舍去

    一.基础知识准备 ​ 奇进偶舍,又称为四舍六入五成双规则.银行进位法(Banker's Rounding),是一种计数保留法,是一种数值修约规则.从统计学的角度,"奇进偶舍"比&q ...

  3. Android Studio无法连接真机的问题?

    我不说,你可能又浪费半天时间,最后的结果,你可能还是没能解决!!! 现在,一般安卓手机为了安装软件方便,一般都安装了豌豆荚,但是,就是这个豌豆荚占用了Android Studio的ADB端口,导致An ...

  4. iOS 多Target, Other link Flag

    在创建多个马甲包或者多个App间只有很小的差异是使用多Target是一种很好的方法 https://www.jianshu.com/p/18db54655246 1:选中原始的Target, 点击右键 ...

  5. 队列(链式队列)----C语言

    链式队列----用链表实现,链式队列就是一个操作受限的单向链表,如果读者了解单向链表的建立过程,那理解链式队列就很容易了,先回顾一下单向链表的建立过程 (不熟悉单向链表的可以先看看另一片随笔,再回来看 ...

  6. Java并发工具类之CountDownLatch

    CountDownLatch允许一个或则多个线程等待其他线程完成操作. 假如我们有这样的需求:我们需要解析一个excel文件中的多个sheet,我们可以考虑使用多线程,每一个线程解析excel中的一个 ...

  7. JAVA是是如何处理字符的。

    String s = "fs123fdsa";//String变量 byte b[] = s.getBytes();//String转换为byte[] String t = new ...

  8. Java Web之Servlet中response、request乱码问题解决

    Java Web之Servlet中response.request乱码问题解决   一.request请求参数出现的乱码问题 get请求: get请求的参数是在url后面提交过来的,也就是在请求行中, ...

  9. 通过sessionStorage来根据屏幕宽度变化来加载不同的html页面

    因为项目需要,分别写了移动端和PC端的两个html页面,现在需要根据不同的屏幕宽度来加载对应的页面. 先说一下本人的思路-- 刚开始我直接在加载页面的时候判断屏幕宽度,然后加载相应的页面,大家是不是也 ...

  10. POJ 1063

    #include <iostream> using namespace std; int main() { //freopen("acm.acm","r&qu ...