今天在項目的中有一個需求,需要在一個Set類型的集合中删除滿足條件的對象,這時想當然地想到直接調用Set的remove(Object o)方法将指定的對象删除即可,測試代碼:
public class Test {
public static void main(String[] args) {
User user1 = new User();
user1.setId(1);
user1.setName("zhangsan");
User user2 = new User();
user2.setId(2);
user2.setName("lisi");
Set userSet = new HashSet();
userSet.add(user1);
userSet.add(user2);
for (Iterator it = userSet.iterator(); it.hasNext();) {
User user = (User) it.next();
if (user.getId() == 1) {
userSet.remove(user);
}
if (user.getId() == 2) {
user.setName("zhangsan");
}
}
for(Iterator it = userSet.iterator(); it.hasNext(); ) {
User user = (User) it.next();
System.out.println(user.getId() + "=>" + user.getName());
}
}
}
但運作程式的時候,卻發現出錯了:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at test.Test.main(Test.java:23)
從API中可以看到List等Collection的實作并沒有同步化,如果在多 線程應用程式中出現同時通路,而且出現修改操作的時候都要求外部操作同步化;調用Iterator操作獲得的Iterator對象在多線程修改Set的時 候也自動失效,并抛出java.util.ConcurrentModificationException。這種實作機制是fail-fast,對外部 的修改并不能提供任何保證。
網上查找的關于Iterator的工作機制。Iterator是工作在一個獨立的線程中,并且擁有一個 mutex鎖,就是說Iterator在工作的時候,是不允許被疊代的對象被改變的。Iterator被建立的時候,建立了一個記憶體索引表(單連結清單),這 個索引表指向原來的對象,當原來的對象數量改變的時候,這個索引表的内容沒有同步改變,是以當索引指針往下移動的時候,便找不到要疊代的對象,于是産生錯 誤。List、Set等是動态的,可變對象數量的資料結構,但是Iterator則是單向不可變,隻能順序讀取,不能逆序操作的資料結構,當 Iterator指向的原始資料發生變化時,Iterator自己就迷失了方向。
如何才能滿足需求呢,需要再定義一個List,用來儲存需要删除的對象:
List delList = new ArrayList();
最後隻需要調用集合的removeAll(Collection con)方法就可以了。
public class Test {
public static void main(String[] args) {
boolean flag = false;
User user1 = new User();
user1.setId(1);
user1.setName("shangsan");
User user2 = new User();
user2.setId(2);
user2.setName("lisi");
Set userSet = new HashSet();
userSet.add(user1);
userSet.add(user2);
List delList = new ArrayList();
for (Iterator it = userSet.iterator(); it.hasNext();) {
User user = (User) it.next();
if (user.getId() == 1) {
delList.add(user);
}
if (user.getId() == 2) {
user.setName("zhangsan");
}
}
userSet.removeAll(delList);
for(Iterator it = userSet.iterator(); it.hasNext(); ) {
User user = (User) it.next();
System.out.println(user.getId() + "=>" + user.getName());
}
}
}