【问题标题】:In Java, can you modify a List while iterating through it?在 Java 中,您可以在迭代 List 时修改它吗?
【发布时间】:2014-01-05 12:10:08
【问题描述】:

我了解在 Java 中 Collection<E> 在迭代时不应修改,例如删除或添加元素。但是如何改变 List 中的元素呢?例如,如果我们有

List<String> letters = new ArrayList<String>();
letters.add("A");
letters.add("B");
letters.add("C");
int i = 0;

for (String letter : letters) {
    letters.set(i, "D");
    i++;
}

所以,我不是在谈论修改存储在元素中的对象。我说的是改变对象的what。 List 的大小没有改变,但索引处的对象正在改变,所以从技术上讲,List 正在被修改。我的老板声称这段代码很好(而且看起来确实有效),但我仍然认为它不正确。使用 ListIterator 的 set(E e) 方法可能会更好吗?

【问题讨论】:

  • 如果您不打算使用变量字母,为什么还要foreach?为什么不试试看它是否有效?
  • 问这个问题之前你试过了吗?
  • 这在文档中有所说明:“结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值 不是结构修改。”
  • 谢谢邹邹。我发现,“请注意,Iterator.remove 是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改底层集合,则行为未指定。”但你的陈述是我要找的。我在 javadoc 中查找了 List,但它不存在。我在 ArrayList 中看到它。谢谢。

标签: java arraylist


【解决方案1】:

在遍历列表时修改列表中的元素的想法没有错(不要修改列表本身,不推荐),但可以更好地表达为:

for (int i = 0; i < letters.size(); i++) {
    letters.set(i, "D");
}

最后,整个列表将以字母"D" 作为其内容。在这种情况下使用增强的for 循环不是一个好主意,您没有将迭代变量用于任何事情,而且您不能使用迭代变量修改列表的内容。

请注意,上面的 sn-p 没有修改列表的结构 - 意思是:没有添加或删除元素,列表的大小保持不变。简单地将一个元素替换为另一个元素不算作结构修改。这是@ZouZou 在 cmets 中引用的文档的link,它指出:

结构修改是添加或删除一个或多个元素或显式调整后备数组大小的任何操作;仅仅设置元素的值不是结构修改

【讨论】:

  • +1 如果您使用 CopyOnWriteArrayList,您可以在迭代时对其进行修改。
  • 我知道我没有使用迭代变量。这是一个基于使用迭代变量的不同代码的简单示例。我只是想表明该列表在迭代期间发生了变化。我无法轻松找到我正在寻找的“结构修改”的文档!谢谢你和邹邹的回答。
  • 奖励:如果你向后迭代,你可以在迭代时删除元素。
  • 请注意,CopyOnWriteArrayList::set(int index, E element) 会复制该列表。这可能会导致不必要的 GC 压力并增加运行时间。
【解决方案2】:

使用CopyOnWriteArrayList
如果要删除它,请执行以下操作:

for (Iterator<String> it = userList.iterator(); it.hasNext() ;)
{
    if (wordsToRemove.contains(word))
    {
        it.remove();
    }
}

【讨论】:

【解决方案3】:

Java 8 的 stream() 接口提供了一种就地更新列表的好方法。

要安全更新列表中的项目,请使用map()

List<String> letters = new ArrayList<>();

// add stuff to list

letters = letters.stream().map(x -> "D").collect(Collectors.toList());

要安全地移除项目,请使用filter()


letters.stream().filter(x -> !x.equals("A")).collect(Collectors.toList());

【讨论】:

  • 就地写入描述两次不会使任何这些行对列表进行就地修改。 Collectors.toList() 为您创建一个全新的列表,然后在第一个示例中将其存储在 letters 中,并在第二个示例中简单地丢弃。
【解决方案4】:

使用 Java 8 的 removeIf()

为了安全移除,

letters.removeIf(x -> !x.equals("A"));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    • 2010-10-10
    • 2010-12-05
    • 1970-01-01
    • 2014-01-31
    相关资源
    最近更新 更多