迭代过程中如何从Map中删除键值对

假设你有一个Map,例如HashMap或Hashtable,其中包含关键值对(如书籍和价格),并且要删除价格大于40美元的所有书籍,那么在Java中如何呢?许多Java程序员会说他们将遍历Map并检查每个条目,然后使用java.util.Map中的remove(Object key)或remove(Object key,Object value)方法删除值大于40美元。虽然方法是对的,但答案是错误的。是的,我们将遍历Map来检查每个值,但是我们不会使用java.util.Map接口中的两个remove()方法,因为它们会抛出ConcurrentModficationException,当你调用它们来在迭代期间去除映射。相反,我们将使用Iterator.remove()方法删除任何值大于40美元的键值对。

Iterator是一个通用的接口,可以让你浏览任何Collection类(包括Map)的每个元素。虽然Map没有实现Collection接口,但是你不能直接从Map获得一个迭代器,但是你总是可以获得一个Map的视图,然后通过调用keySet()方法从这些集合中获得迭代器。由于java.util.Map不允许重复键,所以返回set 。您也可以通过调用values()方法来获取值的集合,因为值可以在Map中重复,并通过调用entrySet()方法来设置条目。

这些设置和集合由实际地图支持,因此您对此视图所做的任何修改都将反映在原始地图中。除了导航方法,例如hasNext()和next(),Iterator还包含一个remove()方法,用于从正在迭代的Collection中移除当前元素。在迭代过程中,应该使用这个方法从地图中删除任何条目或键值对

尽管如此,java.util.Map接口提供了remove()方法的一个重载版本,例如remove(Object key),它可以用来通过key删除一个映射,并通过remove(Object key,Object value)去除一个键值对,使用Iterator或增强for循环迭代map时,不能使用它们(记住Java 1.5 for循环是使用Iterator本身在内部实现的)。

如果你使用它们来移除映射,你的代码将抛出ConcurrentModfiicationException,即使你正在单线程环境中运行你的代码。是的,“并发”这个词已经让很多Java程序员感到困惑,他们害怕在多线程环境中得到这个异常,但是这里的并发与迭代+任何其他修改了集合结构的操作结合使用。

总之,总是使用Iterator的remove()方法从Map中删除一个键值对,而迭代它。这里是从java.util.Map中移除一个键值对的确切步骤

1)通过调用java.util.Map的keySet()或entrySet()方法获取一组键或一组条目

2)从键设置或入境设置。

3)迭代关键集或入口集。

4)检查每个值,如果满足标准调用iterator.remove()方法

一旦完成迭代,满足去除标准的映射应该已经被删除。现在,让我们看一个完整的Java程序来从Map中删除条目。

Java程序在遍历Map时删除键值对

在这个程序中,我有一个从亚马逊网站上获得的Java书籍和价格的地图。基本上,我们有5本最好的Java书籍和它们的价格,我们的任务是删除所有价格高于39美元的书籍。为了做到这一点,我将遍历Map并在检查书的价格后调用Iterator.remove()方法。

我正在使用entrySet()遍历Map,因为它提供了包含key和value两个参数的条目。如果两者都需要,那么这比使用一组键遍历Map要快,因为您需要执行查找来获取值。

如果这本书的价格高于39美元,那么我们通过调用迭代器的remove()方法来删除这本书。我们通过调用getValue()方法来获得价格。

import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.Map.Entry; 
import java.util.Set;
/* * Java Program to remove key value pair from Map while * iteration. */
public class Demo {
    public static void main(String[] args) throws Exception {
    // create a Map to demonstrate example
         Map<String, Double> priceMap = new HashMap<String, Double>();
        // add some mapping e.g. popular Java books and their prices
        priceMap.put("Effective Java", 41.79);
        priceMap.put("Head First Java", 29.02);
        priceMap.put("Java Concurrency In Practice", 30.67);
        priceMap.put("Java SE 8 for Really Impatient", 31.99);
        priceMap.put("Head First Design Pattern", 39.05);
        // let's remove all books which are greater than 39.00 USD from map
        // get a set of entries
        Set<Entry<String, Double>> setOfEntries = priceMap.entrySet();
        // get the iterator from entry set
        Iterator<Entry<String, Double>> iterator = setOfEntries.iterator();
        // iterate over map
        while (iterator.hasNext()) {
            Entry<String, Double> entry = iterator.next();
            Double value = entry.getValue();
            if (value.compareTo(Double.valueOf(39.00)) > 0) {
                System.out.println("removeing : " + entry);
                iterator.remove();
                // always use remove() method of iterator
                }
        }
    }
}
输出:
移除: Head First Design Pattern=39.05 
移除: Effective Java=41.79

从输出结果可以看出,有效的Java和Head First设计模式都被删除,因为它们的价格高于39美元,但是Map仍然包含其他的Java书籍,例如Head First Java,Java Concurrency in Practice以及Java SE 8 for Real Impatient。

因为我们正在使用Iterator的remove()方法,所以我们的代码也不受ConcurrentModificaitonException的影响。如果您取消注释使用Map.remove()方法的行,那么代码将抛出ConcurrentMdofiicationException,如下所示:

线程“main”中的异常java.util.HashMap 

中的java.util.ConcurrentModificationException 异常$ HashIterator.nextNode(HashMap.java: 1437)

在java.util.HashMap $ EntryIterator.next(HashMap.java:1471)

在java.util.HashMap $ EntryIterator.next(HashMap.java:1469 )

在Demo.main(Demo.java:34)

不要混淆为什么你会得到并发修改异常,即使只有一个线程正在修改集合。这里的并发并不意味着多线程,而是同时执行两个操作,例如迭代和去除。

这就是遍历期间如何从Map中删除键值对的全部内容。您应该总是使用Iterator的remove()方法来从地图上移除任何映射,同时迭代它以避免任何错误。在遍历期间禁止使用Map.remove()方法,因为它引发了ConcurrentMdoficiationException异常。


关于“ 迭代过程中如何从Map中删除键值对 的评论

发表评论