重写后hashcode和重写equals hashcode是怎么调用的

重写hashCode和equals方法 - Home for dream
- 博客频道 - CSDN.NET
2161人阅读
如果你的对象想散列存储的集合中或者想作为散列Map的Key时(HashSet、HashMap、Hashtable等)那么你必须重写equals()方法,这样才能保证唯一性。在重写equals()方法的同时,必须重写hashCode()方法?当然,在这种情况下,你不想重写hashCode()方法,也没有错,但是sun建议这么做,重写hashCode只是技术要求(为了提高效率)。
&&&&& 当在散列集合中放入key时,将自动查看key对象的hashCode值,若此时放置的hashCode值和原来已有的hashCode值相等,则自动调用equals()方法,若此时返回的为true则表示该key为相同的key值,只会存在一份。
&&&&& Object中关于hashCode和equals方法的定义为:
Java代码 &
public boolean equals(Object obj) {
return (this == obj);
public native int hashCode();
&& && 基类的hashCode是一个native方法,访问操作系统底层,它得到的值是与这个对象在内存中的地址有关。
&&&&& Object的不同子类对于equals和hashCode方法有其自身的实现方式,如Integer和String等。
&&&&&&&&&&& equals相等的,hashCode必须相等&
&&&&&&&&&&&&hashCode不等的,则 equals也必定不等。
&&&&&&& &&& hashCode相等的 equals不一定相等(但最好少出现 hashCode相等的情况)。
&&&&& HashMap的put(K, Value)方法提供了一个根据K的hashCode来计算Hash码的方法hash()&
Java代码 &
transient Entry[]
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
//HashMap支持null的key
int hash = hash(key.hashCode());
//根据key的hashCode计算Hash值
int i = indexFor(hash, table.length);
//搜索指定Hash值在对应table中的索引
for (Entry&K,V& e = table[i]; e != e = e.next) {
//在i索引处Entry不为空,循环遍历e的下一个元素
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.
e.recordAccess(this);
return oldV
//若i索引处Entry为null,表明此处还没有Entry
modCount++;
addEntry(hash, key, value, i);
//将key、value添加到i索引处
static int hash(int h) {
h ^= (h &&& 20) ^ (h &&& 12);
return h ^ (h &&& 7) ^ (h &&& 4);
&&&& 对于任意给定的对象,只有它的hashCode()返回值相同,那么程序调用hash(int h)方法所计算得到的Hash码值总是相同的。接下来会调用indexFor(int h, int length)方法来计算该对象应该保存在table数组的哪个索引处。&
Java代码 &
static int indexFor(int h, int length) {
return h & (length-1);
&&&&&它总是通过h & (table.length - 1)来得到该对象的保存位置--而HashMap底层数组的长度总是2的n次方,这样就保证了得到的索引值总是位于table数组的索引之内。
&&&&&当通过key-value放入HashMap时,程序就根据key的hashCode()来觉得Entry的存储位置:若两个Entry的key的hashCode()相同那么他们的存储位置相同;若两个Entry的key的equals()方法返回true则新添加Entry的value将覆盖原有Entry的value,但key不会覆盖;若两个Entry的key的equals()方法返回false则新加的Entry与集合中原有的Entry形成Entry链。
Java代码 &
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry&K,V& e = table[bucketIndex];
table[bucketIndex] = new Entry&K,V&(hash, key, value, e);
if (size++ &= threshold)
//size保存了HashMap中所包含的key-value对的数量
resize(2 * table.length);
//扩充table对象的长度2倍
&&&& HashSet的add(E)的实现是通过HashMap的put方法来实现的。(HashSet内部是通过HashMap来实现的,TreeSet则是通过TreeMap来实现的)
Java代码 &
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry&K,V& e = table[i]; e != e = e.next) {
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
&&& 根据key的hashCode计算器Hash值,然后取得该Hash值在表table的索引,取得该索引i对应的table中的Entry,判断key的equals()。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:314125次
积分:4107
积分:4107
排名:第3173名
原创:115篇
转载:98篇
评论:155条
I am a coder,Ijust want to use my talent to change the way of life.
(1)(2)(10)(2)(1)(1)(3)(7)(14)(2)(2)(13)(17)(24)(28)(43)(17)(24)(1)(1)重写equals方法时为什么需要重写hashCode方法 - CSDN博客
object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true&
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象
(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,
当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,
导致混淆,因此,就也需要重写hashcode()
HashSet判重过程如下:
HashSet不能添加重复的元素,当调用add(Object)方法时候,
首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;
如果已存在则调用Object对象的equals方法判断是否返回true, 如果为true则说明元素已经存在,如为false则插入元素。
举例说明:
就这个程序进行分析,在第一次添加时,调用了hashcode()方法,发现该hashcode不存在,然胡直接将hashcode存入对象中,第二次也一样,先取出hashcode,发现已经存在,然后即比较equals,如果equals,则不添加,否则,添加。hashcode也只用于HashSet/HashMap/Hashtable类存储数据,所以会用于比较,需要重写
总结,自定义类要重写equals方法来进行等值比较,自定义类要重写compareTo方法来进行不同对象大小的比较,重写hashcode方法为了将数据存入HashSet/HashMap/Hashtable类时进行比较hashcode和equals为何要同时重写 - CSDN博客
Java中的equals方法与hashCode方法的区别
我们都知道Java语言是完全面向对象的,在java中,所有的对象都是继承于Object类。Ojbect类中有两个方法equals、hashCode,这两个方法都是用来比较两个对象是否相等的。
java中比较两个对象不是用“==”吗?对于String&a=”hello”;String&b=”hello”,比较两个字符串是否相等,我们都知道必须用equals方法,而不是用==。因为a,b是内存中两个不同的对象,他们指向两个内存中不同的地址,只是两个地址存的内容都相同罢了。如果用==比较,则比较的是两个对象的地址,很显然这里返回false,不是我们需要的。如果想返回true,就必须使用equals方法了。在这里String类里对父类Ojbect的equals方法进行了重写(见String类里的equals方法),比较的不再是地址,而是地址里所存的字符串的值。如果不对equals方法重写,调用的就是父类的equals方法,比较的还是两个对象的地址,同调用==一样。
对于值对象,==比较的是两个对象的值,对于引用对象,比较的是两个对象的地址。默认的equals方法同==,一般来说我们的对象都是引用对象,要重写equals方法。再举一个例子,现在有一个学生对象,有属性学号跟姓名,现在我新建了一个学生对象,又从数据里查出一个学生对象,这两个对象的学号跟姓名都一样,那这两个对象是不是相等呢?一般情况下,除非你有特殊需求要处理,这两个对象是相等的,可如果用==去比较,返回的结果是错误的。这时候我们就必须重写equlas方法了。如果学号是主键,在equals方法里,我们认为只要学号相同,就可以返回true,具体实现建议参考String类里equals书写风格。
hashCode方法也是可以用来比较两个对象是否相等的。但是我们很少使用,应该说是很少直接使用。hashCode方法返回的是一个int值,可以看做是一个对象的唯一编码,如果两个对象的hashCode值相同,我们应该认为这两个对象是同一个对象。一般如果使用java中的Map对象进行存储时,他会自动调用hashCode方法来比较两个对象是否相等。所以如果我们对equals方法进行了重写,建议一定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不同的对象返回不同的hash值。如上面的学生例子,如果学号相同,不管姓名相不相同,返回的hash值一定要是一样的,这时我们的hash值只与学号有关。\
浅谈为何要重写&hashcode()与equals()&
首先,这两个方法都来自于Object对象,根据API文档查看下原意。(1)public&boolean&equals(obj),对于任何非空引用值&x&和&y,当且仅当&x&和&y&引用同一个对象时,此方法才返回&true;注意:当此方法被重写时,通常有必要重写&hashCode&方法,以维护&hashCode&方法的常规协定,该协定声明相等对象必须具有相等的哈希码。(2)public&int&hashCode()&返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable提供的哈希表。
我们知道,如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出,此时,利用equals比较八大包装对象(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它对象都是比较的引用地址。那产生了一个问题,为什么jdk中希望我们在重写equals时,非常有必要重写hashcode呢?
我的理解是hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,导致混淆,因此,就也需要重写hashcode。为了保证这种一致性,必须满足以下两个条件:
(1)当obj1.equals(obj2)为true时,obj1.hashCode()&==&obj2.hashCode()必须为true
(2)当obj1.hashCode()&==&obj2.hashCode()为false时,obj1.equals(obj2)必须为false
下面,通过一个简单的例子来验证一下。
import&java.util.*;
class&BeanA&{
private&int&i;
public&BeanA(int&i)&{
this.i&=&i;
public&String&toString()&{
return&&&&&+&i;
public&boolean&equals(Object&o)&{
BeanA&a&=&(BeanA)&o;
return&(a.i&==&i)&?&true&:&
public&int&hashCode()&{
public&class&HashCodeTest&{
public&static&void&main(String[]&args)&{
HashSet&BeanA&&set&=&new&HashSet&BeanA&();
for&(int&i&=&0;&i&&=&3;&i++){
set.add(new&BeanA(i));
System.out.println(set);
set.add(new&BeanA(1));
System.out.println(set.toString());
System.out.println(set.contains(new&BeanA(0)));
System.out.println(set.add(new&BeanA(1)));
System.out.println(set.add(new&BeanA(4)));
System.out.println(set);
我们在类BeanA中重写了equals和hashcode方法,这样在存储到HashSet数据集中,将保证不会出现重复的数据;如果把这两个方法去掉后,那些重复的数据仍会存入HashSet中,这就与HashSet强调的元素唯一性相违背,大家可以把这两个方法注释掉再运行一下。
因此,我们就可以理解在一些java类中什么情况下需要重写equals和hashcode。比如:在hibernate的实体类中,往往通过一个主键(或唯一标识符)来判断数据库的某一行,这就需要重写这两个方法。因为,Hibernate保证,仅在特定会话范围内,持久化标识(数据库的行)和Java标识是等价的。因此,一旦我们混合了从不同会话中获取的实例,如果希望Set有明确的语义,就必&须实现equals()&和hashCode()。
1),判断两个对象的hashCode是否相等&
如果不相等,认为两个对象也不相等,完毕&
如果相等,转入2)
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。后面会重点讲到这个问题。)&
2),判断两个对象用equals运算是否相等&
如果不相等,认为两个对象也不相等&
如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)重写equal 的同时为什么必须重写hashcode_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
喜欢此文档的还喜欢
重写equal 的同时为什么必须重写hashcode
阅读已结束,如果下载本文需要使用
想免费下载本文?
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢RT:
在重写了对象的equals方法后,还需要重写hashCode方法吗
最近在看集合,对此有疑问,请高手回答。
采纳的答案
我比较同意walsh的说法:
首先说建议的情况:& 比如你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
然后再说说必须重写hashCode()的情况:
&&& 如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。
这里我想给楼主讲讲sun的设计者为什么需要设计hashcode方法,也许这样你就应该知道什么时候该重写它了。
数据结构有一种为了提高查找的效率而存在的数据结构——散列表,散列表其实是普通数组概念的推广,因为可以对数组进行直接寻址,故可以再O(1)时间内访问数组的任意元素,thinking in java中有个对hashmap简单的实现,我们来看看你就明白了:
//: containers/SimpleHashMap.java
// A demonstration hashed Map.
import java.util.*;
import net.mindview.util.*;
public class SimpleHashMap&K,V& extends AbstractMap&K,V& {
// Choose a prime number for the hash table
// size, to achieve a uniform distribution:
static final int SIZE = 997;
// You can't have a physical array of generics,
// but you can upcast to one:
@SuppressWarnings("unchecked")
LinkedList&MapEntry&K,V&&[] buckets =
new LinkedList[SIZE];[color=red]//List数组里每项是个List,数组下标是hashcode方法的返回值再经过散列函数得到的,相当于散列表的关键字,它亦代表着每个对象的关键字。(不能显示new一个泛型数组,但是你可以new一个泛型数组的引用,如有需要以后可以将普通数组转型为泛型数组)。[/color]
public V put(K key, V value) {[color=red]//将这个对键值放进hashmap中。[/color]
V oldValue =
int index = Math.abs(key.hashCode()) % SIZE;[color=red]//这里就是通过散列函数对hashcode的返回值处理得到一个关键字,它代表了对象在数组里的位置,既是数组下标。[/color]
if(buckets[index] == null)
buckets[index] = new LinkedList&MapEntry&K,V&&();[color=red]//如果是第一次散列到这个数组下标,那么就新生成一个LinkedList,可以看到它里面保存的是MapEntry&K,V&键和值。[/color]
LinkedList&MapEntry&K,V&& bucket = buckets[index];[color=red]//将这个LinkedList赋值给一个bucket(桶),以后就直接在这个bucket进行操作。[/color]
MapEntry&K,V& pair = new MapEntry&K,V&(key, value);
boolean found =
ListIterator&MapEntry&K,V&& it = bucket.listIterator();
while(it.hasNext()) {
MapEntry&K,V& iPair = it.next();
if(iPair.getKey().equals(key)) {[color=red]//如果已经存在同一个键值,那么就更新value。[/color]
oldValue = iPair.getValue();
it.set(pair); // Replace old with new
if(!found)
buckets[index].add(pair);[color=red]//如果是一个新的键值,那么直接添加到这个LinkedList中。[/color]
return oldV
public V get(Object key) {[color=red]//看hashmap是如何凭借hashcode方法快速定位到键值。[/color]
int index = Math.abs(key.hashCode()) % SIZE;[color=red]//与put方法中的作用一样,生成数组下标,因为我存的时候就是存到这个地方的,那么我取的时候直接访问buckets[index]。[/color]
if(buckets[index] == null)[color=red]//直接访问这个数组下标的LinkedList,如果为null,则返回。[/color]
for(MapEntry&K,V& iPair : buckets[index])[color=red]//为什么要用LinkedList,因为hashcode方法产生的散列码不能完全确定一个对象,也就是说会和其他对象发生“碰撞”,即散列到同一个数组下标,解决这个问题的组号办法就是定义一个List把它们保存起来,但是在这个List中,我们必须保证能用equals方法确定对象的身份,这也就是为什么很多人说hashcode()相等,equals()不一定相等,而equals()相等的两个对象,hashcode()一定相等。所以这里要直接在LinkedList执行线性查找。[/color]
if(iPair.getKey().equals(key))
return iPair.getValue();
public Set&Map.Entry&K,V&& entrySet() {
Set&Map.Entry&K,V&& set= new HashSet&Map.Entry&K,V&&();
for(LinkedList&MapEntry&K,V&& bucket : buckets) {
if(bucket == null)
for(MapEntry&K,V& mpair : bucket)
set.add(mpair);
public static void main(String[] args) {
SimpleHashMap&String,String& m =
new SimpleHashMap&String,String&();
m.putAll(Countries.capitals(25));
System.out.println(m);
System.out.println(m.get("ERITREA"));
System.out.println(m.entrySet());
} /* Output:
{CAMEROON=Yaounde, CONGO=Brazzaville, CHAD=N'djamena, COTE D'IVOIR (IVORY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Libreville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, GHANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa}
[CAMEROON=Yaounde, CONGO=Brazzaville, CHAD=N'djamena, COTE D'IVOIR (IVORY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Libreville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, GHANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa]
怎样?现在应该知道hashcode方法的作用了吧,它就是用来提高效率的,有句话说得好:为速度而散列。因为散列的Set和Map是基于hashcode方法来查找对象的,所以你在使用这些类的时候一定要覆盖hashcode方法,而非散列的Set和Map,例如TreeSet,TreeMap等,它们只需equals方法就可以唯一确定对象的身份。这样说,想必已经很清楚了吧。
楼主,遇到[color=red]可以跳过,不小心填上的
不好意思,上面写的:
引用首先说建议的情况:&&& 比如你的对象想放到Set集合或者是想作为Map的key时,那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
应该加上一个条件,修改如下:
首先说建议的情况:& 比如你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
在Java中,重写equals()方法之后,是否需要重写hashCode()方法,那要看分情况来说明。有些情况下,是建议;有些情况下,是必须重写。
首先说建议的情况:&&& 比如你的对象想放到Set集合或者是想作为Map的key时,那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
必须重写hashCode()的情况:
&&& 如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。
最后明白两点就行了:
1.hashCode()方法存在的主要目的就是提高效率。
2.在集合中判断两个对象相等的条件,其实无论是往集合中存数据,还是从集合中取数据,包括如果控制唯一性等,都是用这个条件判断的,条件如下:
&&& 首先判断两个对象的hashCode是否相等,如果不相等,就认为这两个对象不相等,就完成了。如果相等,才会判断两个对象的equals()是否相等,如果不相等,就认为这两个对象不相等,如果相等,那就认为这两个对象相等。
&&& 上面的条件对于任何集合都是如此,只要理解上面的条件,你就明白了,为什么在有些情况下建议重写hashCode().有些情况下,是必须要重写的,只有一个目的,就是提高效率,你想想,如果你重写了hashCode(),只要不满足第一个条件,那就直接可以判断两个对象是不等的,也就不用花费时间再去比较equals了。
&& 最后总结一句话就是,hashCode()方法存在的主要目的就是提高效率,但是如果你想把对象放到散列存储结构的集合中时,是必须要重写的。
&& 对于如何重写hashCode()方法,自己可以去查文档。
看看这文章,讲得蛮详细的
/topic/257191
Need but not must。
hashCode 顾名思义是一个“散列值码”
散列值,并不能表现其唯一性,但是有离散性,其意义在于类似于进行hashMap等操作时,加快对象比较的速度,进而加快对象搜索的速度。
hashCode 和 equals的关系。
两个对象 equals的时候,hashCode必须相等,但hashCode相等,对象不一定equals。
如果没有重写 hashcode方法,使用Object自带的hashCode,无法保证两个对象equals的时候 hashCode 必须相等的条件。
如果你这里的hashCode是 Model 对象里面 的话,简单的处理方式是返回 ID。
你把下面这段:
引用重写了对象的equals方法后,还需要重写hashCode方法吗
放到google里搜索一下,就OK了。
已解决问题
未解决问题}

我要回帖

更多关于 hashcode与equals区别 的文章

更多推荐

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

点击添加站长微信