在迭代期间向集合添加元素

2022-08-31 11:52:24

是否可以在循环访问集合时向其添加元素?

更具体地说,我想循环访问一个集合,如果一个元素满足某个条件,我想向集合中添加一些其他元素,并确保这些添加的元素也被迭代。(我意识到这可能会导致一个不端接的循环,但我非常确定在我的情况下它不会。

Sun的Java教程表明这是不可能的:“请注意,这是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改了基础集合,则行为未指定。Iterator.remove

因此,如果我无法使用迭代器执行我想做的事情,您建议我做什么?


答案 1

使用要迭代的元素构建队列怎么样?当您要添加元素时,请在队列末尾将它们排队,并继续删除元素,直到队列为空。这就是广度优先搜索通常的工作方式。


答案 2

这里有两个问题:

第一个问题是,在返回之后添加到 。如前所述,在修改基础时没有定义的行为,如以下文档所述:CollectionIteratorCollectionIterator.remove

...如果在迭代过程中以调用此方法以外的任何方式修改了基础集合,则未指定迭代器的行为。

第二个问题是,即使可以获得 an,然后返回到原来的同一元素,也不能保证迭代的顺序,如 Collection.iterator 方法文档中所述:IteratorIterator

...没有关于元素返回顺序的保证(除非此集合是提供保证的某个类的实例)。

例如,假设我们有 列表 。[1, 2, 3, 4]

假设是在 at 时添加的,并且不知何故,我们得到了一个可以从 中恢复迭代的。但是,之后没有保证书。迭代顺序可能是 -- 则迭代器仍将错过元素 。5Iterator3Iterator454[5, 1, 2, 3, 4]5

由于不能保证这种行为,因此不能假设事情会以某种方式发生。

一种替代方法是使用一个单独的元素,可以将新创建的元素添加到其中,然后迭代这些元素:Collection

Collection<String> list = Arrays.asList(new String[]{"Hello", "World!"});
Collection<String> additionalList = new ArrayList<String>();

for (String s : list) {
    // Found a need to add a new element to iterate over,
    // so add it to another list that will be iterated later:
    additionalList.add(s);
}

for (String s : additionalList) {
    // Iterate over the elements that needs to be iterated over:
    System.out.println(s);
}

编辑

详细阐述Avi的答案,可以将我们要迭代的元素排队到队列中,并在队列具有元素时删除元素。这将允许除原始元素之外对新元素进行“迭代”。

让我们看看它是如何工作的。

从概念上讲,如果我们在队列中有以下元素:

[1, 2, 3, 4]

而且,当我们删除 时,我们决定添加 ,队列将如下所示:142

[2, 3, 4, 42]

由于队列是 FIFO(先进先出)数据结构,因此这种排序是典型的。(如队列接口的文档中所述,这不是 必需的。以PriorityQueue为例,它按自然排序对元素进行排序,因此这不是FIFO。Queue

下面是一个使用LinkedList队列)的示例,以便遍历所有元素以及在去队列期间添加的其他元素。与上面的示例类似,在删除元素时添加元素:422

Queue<Integer> queue = new LinkedList<Integer>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);

while (!queue.isEmpty()) {
    Integer i = queue.remove();
    if (i == 2)
        queue.add(42);

    System.out.println(i);
}

结果如下:

1
2
3
4
42

正如所希望的那样,出现了我们点击时添加的元素。422