概述
HashSet元素引用的對象的內容發生變化,會導致“元素不屬於集合”的問題。事實上這個元素還在集合裡,但是調用contains方法進行判斷,得到的結果卻是false。
正文
關於變化
這裡所講的變化是指元素引用的對象的內容的變化,但是對象還是這個對象。比如我們定義如下的field
private Set<Set<Integer>> cache = new HashSet<Set<Integer>>();
我們計劃cache裡的每一個元素都是一個Set<Integer>的集合。如果我們取出cache的一個元素,然後往這個元素集合中添加一個Integer元素。對於cache來說,這個元素還是這個元素,但是它的內容已經變化了。
關於校驗標准
/**
* 校驗.<br>
* 從集合中取出的元素反而不屬於該集合,則為無效.
* @return
*/
private boolean validate() {
boolean flag = true;
for ( Set<Integer> ele : cache ) {
if (!cache.contains(ele)) {
flag = false;
System.out.println("無效的元素:" + ele);
}
}
return flag;
}
測試
我們分為三個測試用例:數據初始化測試、直接更新測試、移除新增測試。
一、數據初始化測試
1. 數據初始化
/**
* 初始化數據.
*/
private void init() {
Integer[][] data = {{1, 2}, {3, 4}, {5}};
for (Integer[] ele : data) {
List<Integer> eleList = Arrays.asList(ele);
Set<Integer> eleSet = new HashSet<Integer>(eleList.size());
eleSet.addAll(eleList);
cache.add(eleSet);
}
System.out.println(cache);
}
2. 測試
@Test
public void testInit() {
init();
boolean flag = validate();
System.out.println("對初始化的數據進行校驗,結果:" + flag);
}
3. 輸出結果
[[2, 1], [5], [4, 3]]
對初始化的數據進行校驗,結果:true
二、直接更新測試
1. 更新的方法
/**
* 直接修改.
*/
private void update() {
for (Set<Integer> ele : cache) {
if (ele.contains(5)) {
ele.add(6);
break;
}
}
System.out.println(cache);
}
2. 測試
@Test
public void testUpdate() {
init();
update();
boolean flag = validate();
System.out.println("對直接修改的數據進行校驗,結果:" + flag);
}
3. 輸出結果
[[2, 1], [5], [4, 3]]
[[2, 1], [6, 5], [4, 3]]
無效的元素:[6, 5]
對直接修改的數據進行校驗,結果:false
三、移除新增測試
1. 移除新增
/**
* 移除添加.
*/
private void removeThenAdd() {
for (Set<Integer> ele : cache) {
if (ele.contains(5)) {
cache.remove(ele);
ele.add(6);
cache.add(ele);
break;
}
}
System.out.println(cache);
}
2. 測試
@Test
public void testRA() {
init();
removeThenAdd();
boolean flag = validate();
System.out.println("對移除添加的數據進行校驗,結果:" + flag);
}
3. 輸出結果
[[2, 1], [5], [4, 3]]
[[2, 1], [4, 3], [6, 5]]
對移除添加的數據進行校驗,結果:true
結論
我認為HashSet遍歷元素和判斷元素是否在集合中的機制是不同的,HashSet中的元素都有一個不同的hashcode,我們直接修改其中的元素,導致其內容和其hashcode對應不上,所以才會有上述的問題。