it-swarm.com.ru

как удалить объект из потока в методе foreach?

мне нужно массивы: arrA и arrB. arrA и arrB являются списками объектов различных типов, а функция add преобразует объекты A в объекты B. Я хочу добавить каждый объект из arrA в arrB и удалить этот объект из arrA. Я пытаюсь сделать это потоком:

arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);});

когда я выполняю это, происходят две вещи:

  1. не все объекты передаются из arrA в arrB.
  2. после нескольких итераций выдается исключение нулевого указателя.

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

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

Каково было бы правильное решение этой проблемы?

РЕДАКТИРОВАТЬ . Дополнительная информация: В реальной реализации этот список, если ранее был отфильтрован

arrA.stream().filter(some condition).foreach(c -> {arrB.add(c); arrA.remove(c);});

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

7
Akka Jaworek

Потоки предназначены для более функционального использования, предпочтительно рассматривая ваши коллекции как неизменные.

Путь без потоков будет:

arrB.addAll(arrA);
arrA.clear();

Тем не менее, вы можете использовать Streams, чтобы фильтровать входные данные, так что это больше похоже на:

arrB.addAll(arrA.stream().filter(x -> whatever).toList())

затем удалите из arrA (спасибо @Holgar за комментарий).

arrA.removeIf(x -> whatever)

Если ваш предикат дорогой, то вы можете разделить:

Map<Boolean, XXX> lists = arrA.stream()
  .collect(Collectors.partitioningBy(x -> whatever));
arrA = lists.get(false);
arrB = lists.get(true);

или составьте список изменений:

List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);
15
davidsheldon

Как уже упоминали другие, это невозможно с foreach - так как невозможно с циклом for (A a: arrA) удалить элементы.

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

Iterator<A> it = arrA.iterator()
while (it.hasNext()) {
    A a = it.next();
    if (!check(a))
        continue;
    arrB.add(a);
    it.remove();
}

Это также избавляет вас от копирования/клонирования arrA.

4
Martin Nyolt

Я не думаю, что вы можете удалить из arrA, пока вы перебираете его.

Вы можете обойти это, обернув его в новый ArrayList <> ();

новый ArrayList <> (arrA) .stream (). foreach (c -> {arrB.add (c); arrA.remove (c);});

1
Mike Samaras

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

Правильно. цикл for-each похож на обычный цикл for, но его проще писать и читать. Вы можете думать об этом как о синтаксическом сахаре. Внутренне он будет использовать итератор или индексы массива. Метод потоков forEach является более причудливой версией, которая допускает параллельное выполнение и функциональный стиль кодирования, но имеет свои недостатки .

Как и в случае любого индексированного цикла, удаление элемента во время цикла прерывает цикл. Подумайте о наличии трех элементов с индексами 0, 1 и 2. Когда вы удаляете элемент 0 в первой итерации, элементы списка будут сдвигаться на один уровень вверх, а в следующей итерации у вас будут элементы 0 (ранее 1) и 1 (ранее 2) , Ваша переменная цикла теперь указывает на 1, поэтому она пропускает фактически следующий элемент. Когда он попадает в индекс 2, в цикле, над которым вы работаете, остался только один элемент (вы удалили два), что выдает ошибку, поскольку индекс выходит за пределы.

Возможные решения:

  • Используйте методы List для клонирования и очистки списков.
  • Сделайте это с двумя циклами, если вам действительно нужно вызывать методы для каждого отдельного элемента.
1
Hubert Grzeskowiak