深度解析hashcode
1.hashcode的取值范围?
2.hashcode和equals的区别?
对于第一个问题,hashcode返回类型为int,则知取值范围为:
[Java] 纯文本查看 复制代码 Integer.MIN_VALUE ---- Integer.MAX_VALUE
int最大值最小值,根据编译器类型不同而变化:
[Java] 纯文本查看 复制代码 public static void main(String[] args) {
// hashcode 取值范围的问题。
//int类型的最大值和最小值,根据编译器的不同而取值范围有所不同
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
int a = 0x7FFFFFFF;
int b = 0x7FFFFFFF+1;
//Integer.MIN_VALUE : 0x7FFFFFFF == 2^31-1
//Integer.MAX_VALUE : 0x7FFFFFFF +1 == -2^31
System.out.println(a);
System.out.println(b);
}
hashcode的取值范围在32以上编译器为 -2^31 到 2^31-1,java采用的是2进制补码机制,第一位符号位,最大值为0x7FFFFFFF,即(2147483647)。
第二个问题示例说明:
[Java] 纯文本查看 复制代码 public class Emp {
private int empno;
private String empname;
private String dept;
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public Emp(int empno, String empname, String dept) {
super();
this.empno = empno;
this.empname = empname;
this.dept = dept;
}
public Emp() {
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Emp [empno=" + empno + ", empname=" + empname + ", dept="
+ dept + "]";
}
@Override
public int hashCode() {//注意,这里重写与否决定于结果
final int prime = 31;
int result = 1;
result = prime * result + empno;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Emp other = (Emp) obj;
if (empno != other.empno)
return false;
return true;
}
}
用hashmap测试:
[Java] 纯文本查看 复制代码 public class TestHashMap {
public static void main(String[] args) {
Emp emp1= new Emp(1000,"张三","xx");
System.out.println(emp1.hashCode());
HashMap<Emp, String> hm = new HashMap<Emp,String>();
hm.put(emp1, "hello");
//修改对象emp1来判断是否hashcode是否发生变化
emp1.setEmpno(10001);
emp1.setDept("李四光");
System.out.println(emp1.hashCode());
System.out.println(hm.get(emp1));
}
}
Emp中不重写时输出结果:
[Java] 纯文本查看 复制代码 1134517053
1134517053
hello
Emp中重写时输出结果:
[Java] 纯文本查看 复制代码 1031
10032
null
结论:
这里Emp类中如果用jdk提供的默认hashcode方法,你会发现仍能够取到value值 hello,尽管key发生了变化(这里的变化是对象值发生了变化,但是地址没有变);
但是如果在Emp类中重写hashcode,会发现取值为空;
猜想可能源于jdk的默认的hashcode方法产生的值是以对象的地址生成的,而跟对象的值没有任何关系。所以为什么上面对象改变了,但是hashcode 是一样的。
这里可以延伸另外一个问题,hashcode 和 equals比较的问题,equals相等则 hashcode一定相等,但是hashcode相等equals未必相等,上面的示例就很好的证明了这个结论:
equals相等则 地址必定相同,地址相同则产生的 hashcode值 一定相等;
hashcode相等,可能是相同地址的不同对象产生的,所以equals有可能不同的。
实战:
对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
这种大量的并且快速的对象对比一般使用的hash容器中,比如hashset,hashmap,hashtable等等,比如hashset里要求对象不能重复,则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先hashCode(),如果hashCode()相同,再用equal()验证,如果hashCode()都不同,则肯定不同,这样对比的效率就很高了。
测试HashSet
[Java] 纯文本查看 复制代码 public class TestHashSet {
public static void main(String[] args) {
HashSet<Emp> set = new HashSet<Emp>();
Emp emp1= new Emp(1000,"zhangsan","xx");
Emp emp2= new Emp(1000,"zhangsan","xx");
set.add(emp1);
set.add(emp2);
System.out.println(set.size());
}
}
不重写hashcode的情况下,emp1和emp2因为地址不相同,产生hashcode是不一样的,根据上面的结论,由于hashcode是不一样的,则直接认定是两个对象放到set中,不再执行equals方法进行判断;
输出结果为:2.
重写hashcode 和 equals 情况下 , emp1和emp2默认也是会认为是两个对象,虽然地址不相同,但调用重写的hashcode后,产生的hashcode值是相同的,根据上面的结论,由于hashcode是一样的,会再根据equals来进行判断是否是相同对象。
输出结果为:1.
tip:
此处说的hashcode()是Object的native方法,如果是String,由于重写hashcode(), 则与值产生了关系,而与地址没有关系,String 的 hashcode()源码如下:
[Java] 纯文本查看 复制代码 public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
---------------------
作者:迎风奔跑
来源:CSDN
原文:https://blog.csdn.net/yingfeng612/article/details/80758886
版权声明:本文为博主原创文章,转载请附上博文链接!
|