Добавление элементов в коллекцию во время итерации - PullRequest
73 голосов
/ 14 июня 2009

Можно ли добавить элементы в коллекцию при переборах по ней?

Более конкретно, я хотел бы перебрать коллекцию, и если элемент удовлетворяет определенному условию, я хочу добавить некоторые другие элементы в коллекцию и убедиться, что эти добавленные элементы также повторяются. (Я понимаю, что это может привести к бесконечному циклу, но я уверен, что в моем случае этого не произойдет.)

Учебное пособие Java от Sun предполагает, что это невозможно: «Обратите внимание, что Iterator.remove - это безопасный только безопасный способ изменения коллекции во время итерации; поведение не определено, если базовая коллекция изменяется любым другим способом, пока выполняется итерация. "

Так что, если я не могу сделать то, что я хочу, используя итераторы, что вы предлагаете мне сделать?

Ответы [ 18 ]

0 голосов
/ 21 августа 2017

Несмотря на то, что мы не можем добавлять элементы в один и тот же список во время итерации, мы можем использовать flatMap Java 8, чтобы добавить новые элементы в поток. Это можно сделать при условии. После этого добавленный элемент может быть обработан.

Вот пример Java, который показывает, как добавить в текущий поток объект в зависимости от условия, которое затем обрабатывается с условием:

List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);

intList = intList.stream().flatMap(i -> {
    if (i == 2) return Stream.of(i, i * 10); // condition for adding the extra items
    return Stream.of(i);
}).map(i -> i + 1)
        .collect(Collectors.toList());

System.out.println(intList);

Пример игрушечного примера:

[2, 3, 21, 4]

0 голосов
/ 21 июня 2014

Я устал от ListIterator, но это не помогло моему случаю, когда вы должны использовать список при добавлении в него. Вот что у меня работает:

Использовать LinkedList .

LinkedList<String> l = new LinkedList<String>();
l.addLast("A");

while(!l.isEmpty()){
    String str = l.removeFirst();
    if(/* Condition for adding new element*/)
        l.addLast("<New Element>");
    else
        System.out.println(str);
}

Это может дать исключение или натолкнуться на бесконечные циклы. Однако, как вы упомянули

Я уверен, что в моем случае этого не будет

ответственность за проверку угловых случаев в таком коде лежит на вас.

0 голосов
/ 14 января 2015

Это то, что я обычно делаю с коллекциями, такими как наборы:

Set<T> adds = new HashSet<T>, dels = new HashSet<T>;
for ( T e: target )
  if ( <has to be removed> ) dels.add ( e );
  else if ( <has to be added> ) adds.add ( <new element> )

target.removeAll ( dels );
target.addAll ( adds );

Это создает некоторую дополнительную память (указатели для промежуточных наборов, но дублирования элементов не происходит) и дополнительные шаги (повторение изменений), однако обычно это не так уж и сложно, и это может быть лучше, чем работа с начальным Коллекционная копия.

0 голосов
/ 22 июля 2013

Забудьте об итераторах, они не работают для добавления, только для удаления. Мой ответ относится только к спискам, поэтому не наказывайте меня за то, что я не решил проблему с коллекциями. Придерживайтесь основ:

    List<ZeObj> myList = new ArrayList<ZeObj>();
    // populate the list with whatever
            ........
    int noItems = myList.size();
    for (int i = 0; i < noItems; i++) {
        ZeObj currItem = myList.get(i);
        // when you want to add, simply add the new item at last and
        // increment the stop condition
        if (currItem.asksForMore()) {
            myList.add(new ZeObj());
            noItems++;
        }
    }
0 голосов
/ 08 февраля 2013

Имея список List<Object>, который вы хотите перебрать, простой-простой способ:

while (!list.isEmpty()){
   Object obj = list.get(0);

   // do whatever you need to
   // possibly list.add(new Object obj1);

   list.remove(0);
}

Итак, вы просматриваете список, всегда берете первый элемент, а затем удаляете его. Таким образом, вы можете добавлять новые элементы в список во время итерации.

0 голосов
/ 14 июня 2009

В общем , это небезопасно, хотя для некоторых коллекций это может быть. Очевидная альтернатива - использовать какой-то цикл for. Но вы не сказали, какую коллекцию вы используете, так что это может или не может быть возможным.

0 голосов
/ 14 июня 2009

ИМХО, более безопасный способ - создать новую коллекцию, выполнить итерацию по заданной коллекции, добавить каждый элемент в новую коллекцию и добавить дополнительные элементы по мере необходимости в новую коллекцию, наконец, вернув новую коллекцию. 1001 *

0 голосов
/ 15 июня 2009

Помимо решения использования дополнительного списка и вызова addAll для вставки новых элементов после итерации (например, решение пользователя Nat), вы также можете использовать одновременные коллекции, такие как CopyOnWriteArrayList .

Метод итератора в стиле «снимок» использует ссылку на состояние массива в момент создания итератора. Этот массив никогда не изменяется в течение времени жизни итератора, поэтому вмешательство невозможно, и итератор гарантированно не генерирует исключение ConcurrentModificationException.

С помощью этой специальной коллекции (обычно используемой для одновременного доступа) можно манипулировать базовым списком, перебирая его. Однако итератор не будет отражать изменения.

Это лучше, чем другое решение? Наверное, нет, я не знаю издержек, связанных с подходом копирования при записи.

...