了解hashcode
public class HashcodeTest {
public static void main(String[] args) {
Integer i1=new Integer(100);
int j = 100;
System.out.println(i1 == j); //true
System.out.println(i1.hashCode());//100
System.out.println(System.identityHashCode(i1));//打印地址
System.out.println(System.identityHashCode(j));
Integer i2=new Integer(128);
System.out.println(i2.hashCode());//128
Integer i3=128;
System.out.println(i3.hashCode());//128
//integer自动拆装箱范围为-128-127,在范围内直接到常量池中取
System.out.println(i2==i3);//false
System.out.println(i2.equals(i3));//true
System.out.println(System.identityHashCode(i2));//打印地址
System.out.println(System.identityHashCode(i3));
}
}
结果:
equals和HashCode的关系
- 1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。
- 2.hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
所有对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
为什么重写equals有必要重写hashcode?
打个比方,一个名叫张三的人去住酒店,在前台登记完名字就去了99层100号房间,此时警察来前台找叫张三的这个人住在哪间房,经过查询,该酒店住宿的有50个叫张三的,需要遍历查询,查询起来很不方便。
那么就换另外一种登记方式,前台登记时登记身份证号,警察来前台找身份证号时发现身份证号也存在重复,经过哈希算法进行计算后相同的hashcode值被分到了一个房间然后产生链表,链表查询效率非常慢,然后警察找的时候也会遇到问题。
那么只能换第三种登记方式了,前台登记时同时登记身份证号和名字,这样警察来找的时候同时按照两个条件去查,这样就能直接锁定要找的人在哪个房间。
在程序中:登记的前台好比哈希算法,名字是对比好比 equals 方法,身份证号的对比好比 hashcode 方法只有equals 和 hashcode 都满足的时候才能确保是同一个对象。
当我们重写一个类的 equals 方法时就应当连同重写 hashcode 方法,并且两个方法应满足:
1:一致性,即:当两个对象 equals 比较为 true,那么 hashcode 值应当相等,反之亦然,因为当两个对象hashcode 值相等,但是 equals 比较为 false,那么在 HashMap 中会产生链表,影响查询性能。
2:成对重写,即重写 equals 就应当重写 hashcode。实际上这只是一条规范,如果不这样做程序也可以执行,只不过会隐藏bug。一般一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中,那么重写equals后最好也重写hashCode,否则会导致存储数据的不唯一性(存储了两个equals相等的数据),因为map结构中的key可以是对象,两个对象的值相同但地址不同,则取出value会出现问题。而如果确定不会存储在这些散列结构中,则可以不重写hashCode。
package com.zejian.test;
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<String,Value> map1 = new HashMap<String,Value>();
String s1 = new String("key");
String s2 = new String("key");
Value value = new Value(2);
map1.put(s1, value);
System.out.println("s1.equals(s2):"+s1.equals(s2));
System.out.println("map1.get(s1):"+map1.get(s1));
System.out.println("map1.get(s2):"+map1.get(s2));
Map<Key,Value> map2 = new HashMap<Key,Value>();
Key k1 = new Key("A");
Key k2 = new Key("A");
map2.put(k1, value);
System.out.println("k1.equals(k2):"+k1.equals(k2));
System.out.println("map2.get(k1):"+map2.get(k1));
System.out.println("map2.get(k2):"+map2.get(k2));
}
/**
* 键
* @author zejian
*
*/
static class Key{
private String k;
public Key(String key){
this.k=key;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Key){
Key key=(Key)obj;
return k.equals(key.k);
}
return false;
}
}
/**
* 值
* @author zejian
*
*/
static class Value{
private int v;
public Value(int v){
this.v=v;
}
@Override
public String toString() {
return "类Value的值-->"+v;
}
}
}
结果:
s1.equals(s2):true
map1.get(s1):类Value的值-->2
map1.get(s2):类Value的值-->2
k1.equals(k2):true
map2.get(k1):类Value的值-->2
map2.get(k2):null
equals和==的关系
- equals和==的区别
==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。 - equals 比较的话 先比较地址,如果相等,直接返回true 然后看比较的对象是不是类型相同,如果不是直接返回false
如果是的话,则依次比较里面的内容,如果全部相等,则返回true
(这里需要注意string比较,string为引用类型,不是基本类型,用equal和==比较可能不一样)
基本类型与引用类型区别