1. 程式人生 > >[轉載]Java HashMap 如何正確遍歷並刪除元素

[轉載]Java HashMap 如何正確遍歷並刪除元素

轉載自 https://www.cnblogs.com/zhangnf/p/HashMap.html?utm_source=itdadao&utm_medium=referral

Java HashMap 如何正確遍歷並刪除元素

 

(一)HashMap的遍歷

 

  HashMap的遍歷主要有兩種方式:

  第一種採用的是foreach模式,適用於不需要修改HashMap內元素的遍歷,只需要獲取元素的鍵/值的情況。

複製程式碼

HashMap<K, V> myHashMap;
for (Map.entry<K, V> item : myHashMap.entrySet()){
    K key = item.getKey();
    V val = item.getValue();
    //todo with key and val
    //WARNING: DO NOT CHANGE key AND val IF YOU WANT TO REMOVE ITEMS LATER
}

複製程式碼


 

  第二種採用迭代器遍歷,不僅適用於HashMap,對其它型別的容器同樣適用。

  採用這種方法的遍歷,可以用下文提及的方式安全地對HashMap內的元素進行修改,並不會對後續的刪除操作造成影響。

複製程式碼

for (Iterator<Map.entry<K, V>> it = myHashMap.entrySet().iterator; it.hasNext();){
    Map.Entry<K, V> item = it.next();
    K key = item.getKey();
    V val = item.getValue();
    //todo with key and val
    //you may remove this item using  "it.remove();"
}

複製程式碼


 

 

(二)HashMap之刪除元素

 

  如果採用第一種的遍歷方法刪除HashMap中的元素,Java很有可能會在執行時丟擲異常。

複製程式碼

HashMap<String, Integer> myHashMap = new HashMap<>();
myHashMap.put("1", 1);
myHashMap.put("2", 2);
for (Map.Entry<String, Integer> item : myHashMap.entrySet()){
    myHashMap.remove(item.getKey());
} for (Map.Entry<String, Integer> item : myHashMap.entrySet()){ System.out.println(item.getKey()); }

複製程式碼

  執行上面的程式碼,Java丟擲了 java.util.ConcurrentModificationException 的異常。並附有如下資訊。

at java.util.HashMap$HashIterator.nextNode(Unknown Source)
at java.util.HashMap$EntryIterator.next(Unknown Source)
at java.util.HashMap$EntryIterator.next(Unknown Source)

  可以推測,由於我們在遍歷HashMap的元素過程中刪除了當前所在元素,下一個待訪問的元素的指標也由此丟失了。


  所以,我們改用第二種遍歷方式。

  程式碼如下:

複製程式碼

for (Iterator<Map.Entry<String, Integer>> it = myHashMap.entrySet().iterator(); it.hasNext();){
    Map.Entry<String, Integer> item = it.next();
    //... todo with item
    it.remove();
}
for (Map.Entry<String, Integer> item : myHashMap.entrySet()){
    System.out.println(item.getKey());
}

複製程式碼

  執行結果沒有顯示,表明HashMap中的元素被正確刪除了。


 

(三)在HashMap的遍歷中刪除元素的特殊情況

 

  上述方法可能足以應付多數的情況,但是如果你的HashMap中的鍵值同樣是一個HashMap,假設你需要處理的是 HashMap<HashMap<String, Integer>, Double> myHashMap 時,很不碰巧,你可能需要修改myHashMap中的一個項的鍵值HashMap中的某些元素,之後再將其刪除。

  這時,單單依靠迭代器的 remove() 方法是不足以將該元素刪除的。

  例子如下:

複製程式碼

HashMap<HashMap<String, Integer>, Integer> myHashMap = new HashMap<>();
HashMap<String, Integer> temp = new HashMap<>();
temp.put("1", 1);
temp.put("2", 2);
myHashMap.put(temp, 3);
for (Iterator<Map.Entry<HashMap<String, Integer>, Integer>> 
    it = myHashMap.entrySet().iterator(); it.hasNext();){
    Map.Entry<HashMap<String, Integer>, Integer> item = it.next();
    item.getKey().remove("1");
    System.out.println(myHashMap.size());
    it.remove();
    System.out.println(myHashMap.size());
}

複製程式碼

  結果如下:

1
1

  雖然 it.remove(); 被執行,但是並沒有真正刪除元素。

  原因在於期望刪除的元素的鍵值(即 HashMap<String, Integer> temp )被修改過了。


  解決方案:

  既然在這種情況下,HashMap中被修改過的元素不能被刪除,那麼不妨直接把待修改的元素直接刪除,再將原本所需要的“修改過”的元素加入HashMap。

  想法很好,程式碼如下:

複製程式碼

for (Iterator<Map.Entry<HashMap<String, Integer>, Integer>> 
    it = myHashMap.entrySet().iterator(); it.hasNext();){
    Map.Entry<HashMap<String, Integer>, Integer> item = it.next();
    //item.getKey().remove("1");
    HashMap<String, Integer> to_put = new HashMap<>(item.getKey());
    to_put.remove("1");
    myHashMap.put(to_put, item.getValue());
    System.out.println(myHashMap.size());
    it.remove();
    System.out.println(myHashMap.size());
}

複製程式碼

  但是依然是RE:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.remove(Unknown Source)

  原因在於,迭代器遍歷時,每一次呼叫 next() 函式,至多隻能對容器修改一次。上面的程式碼則進行了兩次修改:一次新增,一次刪除。


  既然 java.util.ConcurrentModificationException 異常被丟擲了,那麼去想辦法拿掉這個異常即可。

  最後的最後,我決定棄HashMap轉投ConcurrentHashMap。將myHashMap定義為ConcurrentHashMap之後,其它程式碼不動。

  執行結果如下:

2
1

  最終,通過ConcurrentHashMap和一些小技巧的使用,變形實現了對被修改過鍵值的元素的刪除。