一、equals()和==
1.以下是Object类中equals()的源码
public boolean equals(Object obj) {
return (this == obj);
}
2.以下是Integer类中equals()的源码
public boolean equals(Object obj) {
if (obj instanceof Integer) {
//返回当前对象的值与参数对象的值的比较
return value == ((Integer)obj).intValue();
}
return false;
}
3.以下是String类中equals()的源码
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
//判断参数是否为String类对象,不是直接返回false
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;
}
由以上源码我们可以得出:
① equal和==最根本的区别在于equal是一个方法,而==是一个运算符。
② 基本数据类型只能用==来进行比较。在object类中equal()方法就是用==实现的,但是对于,Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法,他们比较的是内容(值)。
③ 在String类中equals()也被重写了,因此字符串比较返回true有两种情况(==:地址内容都一致,equals():内容一致)
题外话:下面代码的运行结果是什么?解释一下为什么会有这些差异。
String s1 = "hello";
String s2 = s1 + ",world";
String s3 = "hello" + ",world";
String s4 = "hello,world";
String s5 = new String("hello,world");
System.out.println(s2.equals(s4)); // true
System.out.println(s2==s4); // false
System.out.println(s3==s4); // true
System.out.println(s4==s5); // false
一般情况下,创建字符串对象有两种方式,一种是字面值创建,一种是通过 new 得方式创建,这两者是不一样的。
如果是字面值创建的方式,如 String s4="hello,world",JVM会先去字符串常量池中寻找有没有“hello,world”这个字符串,若有,则将其地址给 s4;若没有,则先在常量池里创建“hello,world”,然后再把地址给s4;而通过 new 的方式创建对象,则是在堆中创建“hello,world”对象,s5 指向这个对象。还是看图吧,比较直观:
字符串的拼接
String 类被 final 修饰,因此字符串不能修改,当两个字符串相加时,是先生成 StringBuilder 对象,然后通过 append() 的方式将两个字符串拼接,再调用 toString() 方法生成新的字符串对象。所以只考虑 s1 和 s2,他们在 JVM 中是这样的:
但是像 String s3 = "hello" + ",world" 这样直接两个字面值相加的,java文件在编译期间就已经将这条语句做了优化,将其直接变成 "hello,world",等到运行的时候就查找字符串常量池,因此 s3 == s4 返回的结果就为 true。
String、StringBuilder、StringBuffer
String 是常量,且每次需要在原来字符串基础上扩展都需要新建对象,导致速度会稍微慢一点,而且占内存,因此对于需要经常扩展的字符串,可以使用 StringBuilder 和 StringBuffer。但是 StringBuilder 和 StringBuffer 又有区别,即使两者很像,在速度上 StringBuilder 会快一些,因为 StringBuffer 的操作加了 synchronized ,即加了锁,使得操作相对比较慢,就举 append() 为例子,StringBuffer 中此方法的源码如下:
所以,一般在单线程的情况下,可以选择使用 StringBuilder;在多线程的情况下可以选择 StringBuffer .
总结
回到一开始的程序,我们可以大致地画出在 JVM 中的存储:二、hashcode()
hashCode()方法给对象返回一个hash code值。
性质:
- 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个integer。
- 如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()方法必须产生同一个integer结果。
- 并不要求根据equals(java.lang.Object)方法不相等的两个对象,调用二者各自的hashCode()方法必须产生不同的integer结果。[1]
。
1.以下是Object类中hashcode()的源码,说明是一个本地方法,它的实现是根据本地机器相关的。大量的实践表明,由Object类定义的hashCode()方法对于不同的对象返回不同的integer
public native int hashCode();
2.以下是String类中hashcode()的源码,可以看出字符串的内容相同hashcode也相同。
public int hashCode() {
int h = hash; //hash默认值是0
//字符串的内容相同hashcode也相同
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;
}
3.以下是AbstractSet类中hashcode()和equals()的源码
在set集合中,jvm就是根据hashcode(),和equals()这两个方法来判断元素是否重复,如果要重写,建议两个方法一起重写,否则可能会出现安全性问题。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
4.以下是AbstractList类中hashcode()和equals()的源码
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
参考:
https://mp.weixin.qq.com/s/2rTxX3Co-2bDR16wZE-WwQ
https://www.cnblogs.com/Qian123/p/5703507.html
-
两个对象equals()结果相同则两者hashcode()也相同;而两者hashcode()相同,equals()结果不一定相同;因此,在Set中判断元素是否重复的其中一个重要原则就是先判断hashcode,若相同再判断equals()是否为true。 ↩