【问题标题】:How to remove item in List?如何删除列表中的项目?
【发布时间】:2016-02-13 13:04:21
【问题描述】:

我在嵌套循环中有两个列表,当我在内部匹配一个项目时,我想将其删除以提高性能。

List<String[]> brandList = readCsvFile("/tmp/brand.csv");
List<String[]> themeList = readCsvFile("/tmp/theme.csv");

for (String[] brand : brandList) {
    for (String[] theme : themeList) {
        if (brand[0].equals(theme[0])) {
            themeList.remove(theme);
        }
    }
}

我收到java.util.ConcurrentModificationException 错误。如果我改成 CopyOnWriteArrayList,报错如下:

CopyOnWriteArrayList<String[]> themeList = (CopyOnWriteArrayList<String[]>)readCsvFile("/tmp/theme.csv");

java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.concurrent.CopyOnWriteArrayList

现在我该怎么办?省略删除?还是别的什么?

我认为这是我需要的:

List<String[]> brandList = readCsvFile("/tmp/brand.csv");
List<String[]> themeList = readCsvFile("/tmp/theme.csv");

for (String[] brand : brandList) {
    List<String[]> toRemove = new ArrayList<String[]>();

    for (String[] theme : themeList) {
        if (brand[0].equals(theme[0])) {
            toRemove.add(theme);
        }
    }

    for (String[] theme : toRemove) {
        themeList.removeAll(theme);
    }
}

【问题讨论】:

    标签: list classcastexception concurrentmodification copyonwritearraylist


    【解决方案1】:

    您无法在迭代 Collection 时从其中删除项目,Java 中的 foreach 循环本质上就是这样做的。您必须创建一个新的List&lt;String[]&gt; 并收集您希望删除的所有元素,然后在您遍历Collection 后批量删除它们:

    List<String[]> brandList = readCsvFile("/tmp/brand.csv");
    List<String[]> themeList = readCsvFile("/tmp/theme.csv");
    List<String[]> toRemove = new ArrayList<String[]>();
    
    for (String[] brand : brandList) {
        for (String[] theme : themeList) {
            if (brand[0].equals(theme[0])) {
                toRemove.add(theme);
            }
        }
    }
    themeList.removeAll(theme);
    

    【讨论】:

    • 你的代码不是我想要的,我可以把themeList.removeAll(toRemove);在外循环内?
    • 将它放在循环之外更有效,因为对所有要删除的元素只执行一次删除操作。这当然是可能的,但从这段代码片段中我看不出语义差异在哪里。
    【解决方案2】:

    它不是那么漂亮,但你可以使用迭代器来做到这一点:

    List<String[]> brandList = readCsvFile("/tmp/brand.csv");
    List<String[]> themeList = readCsvFile("/tmp/theme.csv");
    
    for (String[] brand : brandList) {
        Iterator<String[]> themeIterator = themeList.iterator();
        while (themeIterator.hasNext()) {
            String[] theme = themeIterator.next();
            if (brand[0].equals(theme[0])) {
                themeIterator.remove();
                // If you are sure there is only one theme per brand, add a break here
                // break;
            }
        }
    }
    

    根据List&lt;&gt; themeList 的具体类型(数组列表、链表等),这可能会或可能不会比复制变体更快。

    【讨论】:

    • 一个品牌有多个主题!
    • 这就是为什么中断被注释掉的原因:)
    • 此代码也会生成ConcurrentModificationException。此外,foreach 循环在内部只不过是一个Iterator
    • 使用迭代器的要点是iterator.remove() 方法允许在迭代期间删除项目(与collection.remove(x) 相比)。有一定的限制,例如当remove() 被调用时,列表中必须只有一个迭代器在运行。有关详细信息,请参阅使用的具体集合的文档。如您所知,虽然它使用了迭代器,但foreach 语法并未公开此方法。
    【解决方案3】:

    如果您使用的是 Java 8 功能,则可能会使用类似这样的功能,并且可能会更快:

    List<String[]> brandList = readCsvFile("/tmp/brand.csv");
    List<String[]> themeList = readCsvFile("/tmp/theme.csv");
    
    // Extract unique values of the first column from the brand list
    // into a structure suited for fast lookup
    Set<String> names = brandList.stream()
            .map(columns -> columns[0])
            .collect(Collectors.toSet())
    
    // Remove all entries from themeList where the value of the
    // first column exists in names
    themeList.removeIf(columns -> names.contains(columns[0]))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-19
      • 2016-03-12
      • 1970-01-01
      • 2022-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多