【问题标题】:Java how to remove element from List efficientlyJava如何有效地从List中删除元素
【发布时间】:2014-05-20 01:07:24
【问题描述】:

好的,这是困扰我好几天的概念验证:

假设我有:

List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");

for (int i = 0; i < a.size(); i++)
{
    String str = a.get(i);
    if (!str.equals("foo") || !str.equals("bar")) a.remove(str);
}

这将以列表 ["foo", "bazz", "bar"] 结束,因为它会读取索引 1 处的字符串 ("buzz"),删除它,索引 2 处的字符串 ("bazz" ) 会跳转到索引 1,并且会被绕过而不经过验证。

我想到的是:

List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");

for (int i = 0; i < a.size(); i++)
{
    String str = a.get(i);
    boolean removed = false;
    if (!str.equals("foo") || !str.equals("bar"))
    {
        a.remove(str);
        removed = true;
    }
    if (removed) i--;
}

它应该以这种方式工作(至少在我的脑海中是这样,哈哈),但是搞乱 for 迭代器并不是一个很好的做法。

我认为的其他方式是创建一个“删除列表”并将需要从列表 a 中删除的项目添加到该列表中,但这只是纯粹的资源浪费。

那么,从列表中有效删除项目的最佳做法是什么?

【问题讨论】:

  • 你应该使用迭代器。
  • 确实我可以删除第二个 if,但是当我想到它时,它有多个 if 可以从列表中删除项目,所以这就是为什么我想到 bool 和第二个 if
  • 索引问题除外。你确定 (!str.equals("foo") || !str.equals("bar")) 。对我来说,这个条件总是通过。
  • @mani 你是对的,它应该是 && 。正如我所说,这是一个概念验证代码,而不是真正的代码,所以我错过了。

标签: java list


【解决方案1】:

改用Iterator 并使用Iterator#remove 方法:

for (Iterator<String> it = a.iterator(); it.hasNext(); ) {
    String str = it.next();
    if (!str.equals("foo") || !str.equals("bar")) {
        it.remove();
    }
}

根据您的问题:

使用 for 迭代器并不是很好的做法

事实上,如果您code oriented to interfaces 并直接使用List 而不是ArrayList,则使用get 方法可能会变成在所有集合中导航以获取所需的元素(例如,如果您有一个@ 987654329@ 由单个链表支持)。因此,这里的最佳实践是使用迭代器而不是使用 get

从列表中有效删除项目的最佳做法是什么?

不仅适用于Lists,而且适用于任何支持IterableCollection,并且假设您没有索引或某种键(例如在Map 中)可以直接访问元素,删除元素的最佳方法是使用Iterator#remove

【讨论】:

  • 谢谢,但在这种情况下,使用 while 循环不是更容易吗?比如: Iterator it = a.iterator(); while (it.hasNext()) { String str = it.next(); // 代码 }
  • @DarkW 您可以使用forwhile 浏览Iterator 的元素。使用您觉得更舒服的方法。
【解决方案2】:

您有三个主要选择:

  1. 使用Iterator,因为它有方便的remove 方法。 :-)

    Iterator<String> it = list.iterator();
    while (it.hasNext()) {
        if (/*...you want to remove `it.next()`...*/) {
            it.remove();
        }
    }
    
  2. 在列表中循环向后,这样如果你删除了一些东西,下一次迭代就没有关系了。这还有一个好处,就是只调用list.size()一次

    for (int index = list.size() - 1; index >= 0; --index) {
        // ...check and optionally remove here...
    }
    
  3. 改为使用while 循环,并且仅在您删除该项目时增加索引变量。

    int index = 0;
    while (index < list.size()) {
        if (/*...you want to remove the item...*/) {
            list.removeAt(index);
        } else {
            // Not removing, move to the next
            ++index;
        }
    }
    

请记住,除非您知道您正在处理ArrayList,否则List#get(int) 的成本可能很高(可能是遍历)。但是如果你知道你正在处理ArrayList(或类似的),那么......

【讨论】:

    【解决方案3】:

    您的第一个示例可能会导致一个错误,因为一旦您删除了一个对象,您的列表索引就会改变。如果您想快速了解它,请使用 iterator 或 List 自己的 .remove() 函数:

    Iterator<String> itr = yourList.iterator();
    while (itr.hasNext()) {
        if ("foo".equals(itr.next()) {
            itr.remove();
        }
    }
    

    或者:

    yourList.remove("foo");
    yourList.removeAll("foo"); // removes all
    

    【讨论】:

      【解决方案4】:

      ArrayList.retainAll 有一个“智能”实现,它做正确的事情是线性时间。您只需使用list.retainAll(Arrays.asList("foo", "bar")),您就可以在这一行中快速实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-16
        • 1970-01-01
        • 2017-12-28
        • 1970-01-01
        • 1970-01-01
        • 2016-10-06
        • 1970-01-01
        • 2011-01-03
        相关资源
        最近更新 更多