String的相等比較
對於String類型而言,一般用“==”或者equales做相等比較,前者比較字符串的引用,後者比較字符串的值。
字符串常量的值存儲於常量池中,只要值相同,那麼引用的就是同一個字符串常量,也就是說,==和equals效果一樣。
字符串對象存儲於堆中,不同的對象在堆上的內存地址是不一樣的。因此,創建兩個值相等的String對象,其引用的地址是不相等的。也就是說,用 == 比較時不等,用equals比較時相等。
Integer的相等比較
Integer是int的包裝類型,通常直接賦值即可。比較時用==還是equals呢?看下面的例子:
Integer t1 = 129;
Integer t2 = 129;
Integer t3 = 119;
Integer t4 = 119;
System.out.println(t1 == t2);
System.out.println(t3 == t4);
輸出結果是false,true。
原因是:對於在-128~127之間的數,Integer使用的是內部緩存值,在此範圍之外的數,Integer會在堆上創建對象。t1和t2是堆上的不同對象,所以引用不相等。而t3和t4是內部緩存的值,引用相等。
顯式new出來的Integer對象,必然會在堆上創建對象,其引用是不同的。
Integer和int進行==比較時,會自動拆箱進行值的比較。
HashMap中key的相等比較
對於HashMap的key,如果是基本數據類型,則直接進行值的比較。而如果是引用類型呢?
HashMap底層是用數組和鏈表存儲的,當鏈表長度超過8時會轉化爲紅黑樹。根據key進行查找時,先根據hashCode找到數組上的位置,如果該位置上有多個元素,則繼續通過equals方法判斷值是否相等。
當兩個複雜對象作爲map的key相等時,必然先要保證hashCode相同,其次equals也要爲true。也就是說,使用複雜對象作爲map的key時,需要重寫hashCode和queals方法。
如果只重寫hashCode方法不重寫equals方法,則默認的equals方法會進行內存地址的比較,必然是不同的。如果不重寫hashCode方法而僅僅重寫equals方法,則首先數組上的位置就不同(當然,發生哈希碰撞時例外),對象自然不會相同。
看下面的例子:
public static class Student {
private String name;
private String id;
public Student(String name) {
this.name = name;
}
public Student(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public static void main(String[] args) throws Exception {
Student stu1 = new Student("aa", "1");
Student stu2 = new Student("aa", "1");
Map<Student, String> map = new HashMap<>();
map.put(stu1,"1");
map.put(stu2,"2");
System.out.println(map.size());
}
輸出爲2。
在這個例子中,map中放入Student類型對象作爲key,如果不重寫比較方法的話,放入的對象是不同的(默認的比較方法繼承於Object類,比較的是內存地址)。
重寫hashCode和queals
對Student類的比較方法進行重寫:
public static class Student {
private String name;
private String id;
public Student(String name) {
this.name = name;
}
public Student(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Student)) {
return false;
}
Student s = (Student) o;
return Objects.equals(id, s.getId()) && Objects.equals(name, s.getName());
}
}
public static void main(String[] args) throws Exception {
Student stu1 = new Student("aa", "1");
Student stu2 = new Student("aa", "1");
Map<Student, String> map = new HashMap<>();
map.put(stu1,"1");
map.put(stu2,"2");
System.out.println(map.size());
}
輸出爲1,說明stu2和stu1相同,添加元素的key相同時,後者覆蓋了前者。
還可以用Apache Commons Lang中的API來實現重寫:
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(name).append(id).toHashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Student)) {
return false;
}
Student s = (Student) o;
return new EqualsBuilder().append(name, s.getName()).append(id, s.getId()).isEquals();
}