Java==equals()和hashCode()的区别
Posted remo0x
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java==equals()和hashCode()的区别相关的知识,希望对你有一定的参考价值。
==
1.用==
进行比较的时候,比较的是变量的值。变量有两种类型:
- 基本类型(int、double等)
- 引用类型(类、数组等)
2.对于基本类型,比较的是字面量
int a = 12;
int b = 12;
System.out.println(a == b);
# 输出:true
3.对于引用类型,比较的是栈中reference的值
int[] a = 1, 2;
int[] b = 1, 2;
System.out.println(a == b);
# 输出:false
4.对于String的比较有特例,JVM会在常量池(包括静态常量池和运行时常量池)中创建第一次出现的字符串字面量,以后所有使用该字面量的String实际上都是常量池中的引用。而用new
创建String的实例时,则变量值是堆上的引用
String a1 = "a";
String a2 = "a";
System.out.println(a1 == a2);
# 输出:true
String a3 = new String("a");
System.out.println(a1 == a3);
# 输出:false
5.对于Integer的比较也有特例。对Integer赋字面量时,编译器会调用valueOf()方法
Integer a = 12;
# 被编译成
Integer a = Integer.valueOf(12);
valueOf()方法的源码如下:
public static Integer valueOf(int i)
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
IntegerCache.low=-128,IntegerCache.high>=127(具体值由sun.misc.VM.getSavedProperty(“java.lang.Integer.IntegerCache.high”)决定),所以[-128,127]范围内的Integer值一定会缓存。即[-128,127]范围内的Integer字面量是引用的缓存中的值,如下所示
Integer a1 = 127;
Integer a2 = 127;
System.out.println(a1 == a2);
# 输出:true
Integer a3 = 128;
Integer a4 = 128;
System.out.println(a3 == a4);
# 输出:false
另外把int和Integer进行比较时会对Integer进行拆箱,比如
int a1 = 12;
Integer a2 = new Integer(12);
System.out.println(a1 == a2);
# 输出:true
equals()
equals()方法是Object类中的,所有对象都有这个方法,默认的equals()方法和==
是同义的,即判断是否是同一个内存地址
public boolean equals(Object obj)
return (this == obj);
源码的注释中对equals()有五点特性要求:
- 自反性:对任意引用值x,x.equals(x)的返回值一定为true
- 对称性:对于任何引用值x、y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true
- 传递性:如果x.equals(y)=true,y.equals(z)=true,则x.equals(z)=true
- 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
- 非空性:任何非空的引用值x,x.equals(null)的返回值一定为false
可以覆盖equals()实现自己的相等逻辑,比如常用的String的equals方法:
public boolean equals(Object anObject)
if (this == anObject)
return true;
if (anObject instanceof String)
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length)
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0)
if (v1[i] != v2[i])
return false;
i++;
return true;
return false;
String中equals()方法判断相等的步骤是:
- 若指向同一个内存地址,即为同一个String实例,返回true
- 如果对象不是String实例,返回false
- 如果value长度不一样,返回false
- 逐个字符比较,若有不相等字符返回false
- 所有字符都相同,返回true
可以从String的equals()方法中总结出覆盖equals()方法的一般形式:
- 使用
==
检查是否指向同一内存地址,如果是则返回true。这是一种性能优化,如果比较操作有可能很昂贵,就值得这么做 - 使用instanceof检查对象是否是正确类型,如果不是则返回false。一般来说,“正确类型”是指equals()方法所在的类
- 把参数转换成正确的类型。因为转换之前进行过instanceof测试,所以确保会成功
- 检查对象中的“关键”域是否对应“相等”。如果这些测试全部成功则返回true,否则返回false
- 当写完equals()方法后,检查是否符合上述五点特性
hashCode()
hashCode()方法是Object类中声明的一个native方法,交由C++实现,将对象的内存地址转为int值返回
public native int hashCode();
源码的注释中对hashCode()方法有三点规范:
- 在应用程序一次执行中,如果用于equals()方法中的域没有被修改,则无论何时调用hashCode(),对于同一个对象总是返回相同的hash值。从应用程序的一次执行到同一应用程序的另一次执行,该hash值不需要保持一致
- 用equals()方法判定相等的两个对象,它们的hashCode()方法也应该返回相同的hash值
- 用equals()方法判定不相等的两个对象,它们的hashCode()方法不必返回不同的hash值。不过不同的对象返回不同的hash值可以提高hash表的性能
可以覆盖Object中的hashCode()方法,用于根据对象的内容生成hash值。比如String类的hashCode()方法:
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;
实现的计算表达式如下所示(s[i]是字符串的第i个字符,n是字符串的长度,^表示求幂。空字符串的hash值是0):
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
对于乘子31的选择,主要有两点原因:
- 31是一个不大不小的质数,是作为hashCode()乘子的优选质数之一,可以均匀分布hash值并减少信息丢失
- 31可以被JVM优化,31 * i = (i << 5) - i
String的hashCode()方法就是根据value生成hash值,这样不同value的字符串就容易生成不同的hash值,既能减少碰撞又能生成与内容相关的hash值
hashCode()方法主要用于hash表,当集合要添加新元素时,大致按如下步骤:
- 先调用该元素的hashCode()方法,直接定位到它应该放置的物理位置上
- 如果这个位置上没有元素,就直接存储在这个位置上
- 如果这个位置上已经有元素,就调用equals()方法进行比较,相同的话就不存,不相同就进行其它相关操作以存储新元素
所以重写equals()方法时,也必须重写hashCode()方法。如果不这样做,就会违反Object.hashCode()的规范,导致无法结合所有基于hash的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable
参考文章
- java中equals,hashcode和==的区别
- String:字符串常量池
- Integer类型与int的==比较
- “==”、equals和hashCode有什么区别
- 科普:为什么 String hashCode 方法选择数字31作为乘子
以上是关于Java==equals()和hashCode()的区别的主要内容,如果未能解决你的问题,请参考以下文章
在java中,关于equals(),和hashCode()的重写问题。