HashCode和equals方法如何决定对象能否存入Hash容器中
hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。
hashCode 的常规协定是:
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
以上这段官方文档的定义,我们可以抽出成以下几个关键点:
1、【hashCode的存在主要是用于查找的快捷性】,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;【eq->hs】
3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;【同事重写】
4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。【hs->eq】
再归纳一下就是hashCode是用于【查找】使用的,而equals是用于【比较】两个对象的是否相等的。以下这段话是从别人帖子回复拷贝过来的:
1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有
例如内存中有这样的位置
0 1 2 3 4 5 6 7
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。
但如果用hashcode那就会使效率提高很多。
我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。
2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。
也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。
那么。重写了equals(),为什么还要重写hashCode()呢?
想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊
public class HashTest {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int hashCode() {
return i % 10;
}
public final static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<HashTest>();
set.add(a);
set.add(b);
System.out.println(a.hashCode() == b.hashCode());
System.out.println(a.equals(b));
System.out.println(set);
}
}
true
false
[[email protected], [email protected]]
重写equals方法
public class HashTest {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int hashCode() {
return i % 10;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HashTest other = (HashTest) obj;
if (i != other.i)
return false;
return true;
}
public final static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<HashTest>();
set.add(a);
set.add(b);
System.out.println(a.hashCode() == b.hashCode());
System.out.println(a.equals(b));
System.out.println(set);
}
}
true
true
//以下个人测试总结,关于以下内容的错误,可以和本人联系/指正
//【温馨提示】:代码验证不重要,主要看总结,应该就明白了
HashMap:
public class HashMapTest {
private int i ;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
return result;
}
// @Override
// public boolean equals(Object obj) {
// if (this == obj)
// return true;
// if (obj == null)
// return false;
// if (getClass() != obj.getClass())
// return false;
// HashMapTest other = (HashMapTest) obj;
// if (i != other.i)
// return false;
// return true;
// }
public static void main(String[] args) {
HashMapTest hashMapTest = new HashMapTest();
hashMapTest.setI(1);
HashMapTest hashMapTest2 = new HashMapTest();
hashMapTest2.setI(1);
Map<HashMapTest,Integer> map = new HashMap<HashMapTest,Integer>();
map.put(hashMapTest, 1);
map.put(hashMapTest2, 2);
System.out.println(hashMapTest);
System.out.println(hashMapTest2);
System.out.println(map);
System.out.println(map.size());
}
}
结果:
[email protected] //调用的toString返回的就是HashCode的值32对应的十六进制0x20
[email protected]
{[email protected]=1, [email protected]=2}
2
public class HashMapTest {
private int i ;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HashMapTest other = (HashMapTest) obj;
if (i != other.i)
return false;
return true;
}
public static void main(String[] args) {
HashMapTest hashMapTest = new HashMapTest();
hashMapTest.setI(1);
HashMapTest hashMapTest2 = new HashMapTest();
hashMapTest2.setI(1);
Map<HashMapTest,Integer> map = new HashMap<HashMapTest,Integer>();
map.put(hashMapTest2, 2);
map.put(hashMapTest, 1);
System.out.println(hashMapTest);
System.out.println(hashMapTest2);
System.out.println(map);
System.out.println(map.size());
}
}
[email protected]
[email protected]
{[email protected]=1}
1
修改hashCode,同意对象竟然也可以放入Set中
public class SetModHashCode {
private static int j;
private int i = 0;
@Override
public int hashCode() {
return j;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SetModHashCode other = (SetModHashCode) obj;
if (i != other.i)
return false;
return true;
}
public static void main(String[] args) {
SetModHashCode setModHashCode = new SetModHashCode();
setModHashCode.setI(12);
//SetModHashCode setModHashCode = new SetModHashCode();
Set<SetModHashCode> set = new HashSet<SetModHashCode>();
set.add(setModHashCode);
//修改hashCode值
SetModHashCode.j = 10;
set.add(setModHashCode);
System.out.println(set);
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
结果:
[[email protected], [email protected]]
【总结】
所以对象能不能存入和对象是否相等没有必然的关系
我以前有个错误的认识,就是equals方法决定了是否能够放入Hash式容器(自己想的一个名字)中
由于java自动位置hashCode常规约定,所以我们没有重写该两个方法时候也是符合常规约定的
1.equals方法比较内存地址
2.hashCode返回的也正好是内存地址
证明如下:
[email protected]
[email protected]
{[email protected]=2, [email protected]=1}
2
77244764 //[email protected]对应hash值 77244764十六进制=49aa95c
1408448235 //[email protected]对应hash值 1408448235十六进制=53f336eb
本质上将一个对象能否存入hash容器第一条件是
hashCode不相等 一定可以存入
hashCode相等 时候再看equals方法
equals方法不相等 可以存入
equals方法相等 坚决不能存入
突然想到hash算法和桶式排序(基数排序的特例)类似性,接下来利用桶式排序的名词和结构特点来说明下(可能有些不合适,但是个人觉得很形象)
hashCode返回的值决定放入那个桶中(有图就一目了然了)
equals方法用来判断桶中是否有相同对象,如果都不在一个桶中,equals方法比较久失去意义,只用当hashCode返回值相等,equals方法才能起到判断对象能否放入容器中作用

这些也是在测试过程总结出来的,刚开始句柄返回的是十六进制和hashCode返回的是十进制对我分析产生了很大影响,迟迟不过敢下结论,最后讲过使用计算器将十六进制转回成十进制才有点相信自己的这些理解,所以这个差异也应该作为一个知识点记住
hashCode 的常规协定是:
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
以上这段官方文档的定义,我们可以抽出成以下几个关键点:
1、【hashCode的存在主要是用于查找的快捷性】,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;【eq->hs】
3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;【同事重写】
4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。【hs->eq】
再归纳一下就是hashCode是用于【查找】使用的,而equals是用于【比较】两个对象的是否相等的。以下这段话是从别人帖子回复拷贝过来的:
1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有
例如内存中有这样的位置
0 1 2 3 4 5 6 7
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。
但如果用hashcode那就会使效率提高很多。
我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。
2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。
也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。
那么。重写了equals(),为什么还要重写hashCode()呢?
想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊
public class HashTest {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int hashCode() {
return i % 10;
}
public final static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<HashTest>();
set.add(a);
set.add(b);
System.out.println(a.hashCode() == b.hashCode());
System.out.println(a.equals(b));
System.out.println(set);
}
}
true
false
[[email protected], [email protected]]
重写equals方法
public class HashTest {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int hashCode() {
return i % 10;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HashTest other = (HashTest) obj;
if (i != other.i)
return false;
return true;
}
public final static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<HashTest>();
set.add(a);
set.add(b);
System.out.println(a.hashCode() == b.hashCode());
System.out.println(a.equals(b));
System.out.println(set);
}
}
true
true
//以上网上copy 其他博主的
http://blog.****.net/fenglibing/article/details/8905007
//以下个人测试总结,关于以下内容的错误,可以和本人联系/指正
//【温馨提示】:代码验证不重要,主要看总结,应该就明白了
HashMap:
public class HashMapTest {
private int i ;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
return result;
}
// @Override
// public boolean equals(Object obj) {
// if (this == obj)
// return true;
// if (obj == null)
// return false;
// if (getClass() != obj.getClass())
// return false;
// HashMapTest other = (HashMapTest) obj;
// if (i != other.i)
// return false;
// return true;
// }
public static void main(String[] args) {
HashMapTest hashMapTest = new HashMapTest();
hashMapTest.setI(1);
HashMapTest hashMapTest2 = new HashMapTest();
hashMapTest2.setI(1);
Map<HashMapTest,Integer> map = new HashMap<HashMapTest,Integer>();
map.put(hashMapTest, 1);
map.put(hashMapTest2, 2);
System.out.println(hashMapTest);
System.out.println(hashMapTest2);
System.out.println(map);
System.out.println(map.size());
}
}
结果:
[email protected] //调用的toString返回的就是HashCode的值32对应的十六进制0x20
[email protected]
{[email protected]=1, [email protected]=2}
2
public class HashMapTest {
private int i ;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HashMapTest other = (HashMapTest) obj;
if (i != other.i)
return false;
return true;
}
public static void main(String[] args) {
HashMapTest hashMapTest = new HashMapTest();
hashMapTest.setI(1);
HashMapTest hashMapTest2 = new HashMapTest();
hashMapTest2.setI(1);
Map<HashMapTest,Integer> map = new HashMap<HashMapTest,Integer>();
map.put(hashMapTest2, 2);
map.put(hashMapTest, 1);
System.out.println(hashMapTest);
System.out.println(hashMapTest2);
System.out.println(map);
System.out.println(map.size());
}
}
[email protected]
[email protected]
{[email protected]=1}
1
修改hashCode,同意对象竟然也可以放入Set中
public class SetModHashCode {
private static int j;
private int i = 0;
@Override
public int hashCode() {
return j;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SetModHashCode other = (SetModHashCode) obj;
if (i != other.i)
return false;
return true;
}
public static void main(String[] args) {
SetModHashCode setModHashCode = new SetModHashCode();
setModHashCode.setI(12);
//SetModHashCode setModHashCode = new SetModHashCode();
Set<SetModHashCode> set = new HashSet<SetModHashCode>();
set.add(setModHashCode);
//修改hashCode值
SetModHashCode.j = 10;
set.add(setModHashCode);
System.out.println(set);
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
结果:
[[email protected], [email protected]]
【总结】
所以对象能不能存入和对象是否相等没有必然的关系
我以前有个错误的认识,就是equals方法决定了是否能够放入Hash式容器(自己想的一个名字)中
由于java自动位置hashCode常规约定,所以我们没有重写该两个方法时候也是符合常规约定的
1.equals方法比较内存地址
2.hashCode返回的也正好是内存地址
证明如下:
[email protected]
[email protected]
{[email protected]=2, [email protected]=1}
2
77244764 //[email protected]对应hash值 77244764十六进制=49aa95c
1408448235 //[email protected]对应hash值 1408448235十六进制=53f336eb
本质上将一个对象能否存入hash容器第一条件是
hashCode不相等 一定可以存入
hashCode相等 时候再看equals方法
equals方法不相等 可以存入
equals方法相等 坚决不能存入
突然想到hash算法和桶式排序(基数排序的特例)类似性,接下来利用桶式排序的名词和结构特点来说明下(可能有些不合适,但是个人觉得很形象)
hashCode返回的值决定放入那个桶中(有图就一目了然了)
equals方法用来判断桶中是否有相同对象,如果都不在一个桶中,equals方法比较久失去意义,只用当hashCode返回值相等,equals方法才能起到判断对象能否放入容器中作用
这些也是在测试过程总结出来的,刚开始句柄返回的是十六进制和hashCode返回的是十进制对我分析产生了很大影响,迟迟不过敢下结论,最后讲过使用计算器将十六进制转回成十进制才有点相信自己的这些理解,所以这个差异也应该作为一个知识点记住