最近在梳理自己所學的Java知識,但是在看到HashSet的時,閒着無聊所做的測試發現原本以爲的成員不會重複的HashSet卻成功添加了重複的元素。代碼如下:
package com.holmes.learn.jihe;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class MyData {
public static void main(String[] args) {
Set<MyData> set = new HashSet<>();
MyData data1 = new MyData("1", "1");
MyData data2 = new MyData("2", "2");
MyData data3 = new MyData("1", "1");
set.add(data1);
set.add(data2);
set.add(data3);
Iterator<MyData> iterator = set.iterator();
while (iterator.hasNext()) {
MyData data = iterator.next();
System.out.println(data + " " + data.hashCode());
}
System.out.println();
data1.setId("4");
set.add(data1);
iterator = set.iterator();
while (iterator.hasNext()) {
MyData data = iterator.next();
System.out.println(data + " " + data.hashCode());
}
System.out.println();
set.add(data3);
iterator = set.iterator();
while (iterator.hasNext()) {
MyData data = iterator.next();
System.out.println(data + " " + data.hashCode());
}
}
private String id;
private String name;
public MyData(){
}
public MyData(String id, String name) {
super();
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyData other = (MyData) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "MyData [id=" + id + ", name=" + name + "]";
}
}
MyData是一個包含id和name兩個String類型的成員變量的類,並通過兩個變量的值重寫了equals和hashCode方法。第一步向HashSet中插入三個元素:
MyData data1 = new MyData("1", "1");
MyData data2 = new MyData("2", "2");
MyData data3 = new MyData("1", "1");
很顯然,根據HashSet的元素不重複可以知道第一次打印的結果爲:
MyData [id=2, name=2] 2561
MyData [id=1, name=1] 2529
data1與data3的值相同,顯然data3無法添加成功。
第二步改變data1的值,再次向set中添加data1,並打印結果,發現了添加成功了。
data1.setId("4");
set.add(data1);
MyData [id=2, name=2] 2561
MyData [id=4, name=1] 2622
MyData [id=4, name=1] 2622
第三步把之前data3再次添加進去,並打印結果,發現再次添加成功。
set.add(data3);
MyData [id=2, name=2] 2561
MyData [id=4, name=1] 2622
MyData [id=1, name=1] 2529
MyData [id=4, name=1] 2622
通過以上實驗發現,HashSet中竟然出現了重複元素。這是爲什麼呢?
看過jdk源碼的都知道,HashSet元素不重複的功能是通過HashMap的key唯一來實現的。再向HashSet中添加對象時,首先通過hashCode值來判斷對象是否相同,如果不同,則直接向set中添加,相同則通過比較equals來比較元素是否相同。同時,hashSet通過hashCode的值來選擇將添加的元素放到相應的“位置”。
根據上述規則可以知道,代碼中,第一次向set中添加data1、data2與data3時,成功添加了data1和data2,但是在修改了data1的值後再次添加的過程中,向判斷元素的hashCode值對應的“位置”是否已有元素存在,顯然是沒有的,所以添加成功了。第三步中添加data3時,計算hashCode時發現“位置”已經被佔了,接着判斷equals的值,結果爲false,所以data3依然添加成功了。
總結:在使用HashSet存儲對象時,發生上述情況會導致內存泄漏,因此在使用時應當注意。當然,出現任何問題我們都能以我們所知道的知識來解釋問題、避免問題,並解決問題。