Java如何有效地从List中删除元素

时间:2014-04-07 16:46:57

标签: java list

好的,这是我头脑中的一个概念验证,它已经困扰了我几天:

让我说我有:

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--;
}

它应该以这种方式工作(至少它在我的头脑中大声笑),但是为迭代器搞乱并不是很好的做法。

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

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

4 个答案:

答案 0 :(得分:3)

使用Iterator代替并使用Iterator#remove方法:

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

从你的问题:

  

搞乱迭代器并不是一个好的做法

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

  

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

不仅适用于List,还适用于支持Iterable的任何Collection,并假设您没有索引或某种键(例如{ {1}})要直接访问元素,删除元素的最佳方法是使用Map

答案 1 :(得分: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;
        }
    }
    
  4. 请记住,除非您知道您正在处理ArrayList,否则List#get(int)的费用可能很高(可能是遍历)。但如果您知道您正在处理ArrayList(或类似),那么......

答案 2 :(得分:1)

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

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

或者:

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

答案 3 :(得分:1)

ArrayList.retainAll有一个“智能”实现,它做正确的线性时间。您可以使用list.retainAll(Arrays.asList("foo", "bar")),然后您将在该行中获得快速实施。