Как удалить элемент из CopyOnWriteArrayList, используя его итератор [duplicate]

11
задан Bick 10 April 2011 в 15:39
поделиться

7 ответов

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

Будет ли это работать для вас? Я имею в виду, не уверен, что логика удаления сложнее, чем в вашем алгоритме.

21
ответ дан Edwin Dalorzo 16 August 2018 в 00:47
поделиться
  • 1
    Я собирался предположить это, но я решил не делать это, поскольку он более подробный и менее эффективный. – Robin Green 10 April 2011 в 15:47
  • 2
    Многословие не должно быть проблемой при условии, что это правильно. Почему вы говорите, что он менее эффективен? Напротив, в этом случае вы принудительно вносите одно изменение в "copy on write" Коллекция & Quot ;. Разве вы так не думаете? – Edwin Dalorzo 10 April 2011 в 15:49
  • 3
    Это более эффективно в этом случае, так как каждая операция remove () создает временный массив, а removeAll () выполняет его только один раз. – Vladimir Dyuzhev 10 April 2011 в 15:51
  • 4
    Владимир. ты прав. хотя я надеялся найти способ повторить итерацию в списке. было бы лучше - только один цикл. и один экземпляр памяти. – Bick 10 April 2011 в 15:54
  • 5
    Будет ли for (int i = arr.size () - 1; i & gt; = 0; i--) работать для вас? Тем не менее, temp-массив является лучшим решением. – Vladimir Dyuzhev 10 April 2011 в 16:29

самый короткий и наиболее эффективный способ:

  List & lt; String & gt;  list = new CopyOnWriteArrayList & lt; & gt; ();  list.removeIf (s - & gt; s.length () & lt; 1);   

внутренне создает временный массив с одинаковой длиной и копирует все элементы, где предикат возвращает true.

помните, что если вы используете этот метод для фактической итерации над элементами для выполнения какого-либо действия, эти действия не могут выполняться в paralell больше, поскольку removeIf-вызов является атомарным и блокирует обход для других потоков

0
ответ дан benez 16 August 2018 в 00:47
поделиться

Поскольку это CopyOnWriteArrayList, совершенно безопасно удалять элементы при повторении с помощью forEach. Нет необходимости в причудливых алгоритмах.

list.forEach (e - & gt; {if (shouldRemove (e)) list.remove (e);});

EDIT: Ну, конечно, это работает, если вы хотите удалить элементы по ссылке, а не по положению.

4
ответ дан Hooman Valibeigi 16 August 2018 в 00:47
поделиться

Вместо списка можно использовать Queue .

  private Queue & lt; Something & gt;  queue = new ConcurrentLinkedQueue & lt; Something & gt; ();   

Он потокобезопасен и поддерживает iterator.remove () . Помните о потокобезопасном поведении итераторов очереди, хотя (проверьте javadoc).

1
ответ дан Nacho Coloma 16 August 2018 в 00:47
поделиться

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

То же, что и для любого другого списка, итератор которого не поддерживает удаление, или что-либо, где вы не используете итератор. Есть три основных метода, которые приходят на ум, чтобы избежать этой ошибки:

  1. Уменьшить индекс после удаления чего-либо (старайтесь не делать ничего с индексом до следующей итерации). Для этого вам, очевидно, придется использовать стиль for (int i = 0; i & lt; ... style for for), чтобы вы могли манипулировать индексом.
  2. Как бы то ни было, повторите то, что делает внутренняя часть цикла, буквально вернувшись к вершине цикла. Немного взлома - я бы избегал этой техники.
  3. Итерации по списку в обратном порядке (от конца до start, а не от начала до конца). Я предпочитаю этот подход, поскольку он самый простой.
6
ответ дан Robin Green 16 August 2018 в 00:47
поделиться
  • 1
    номер три был моим счастливым победителем тоже. Но как мне повторить. в списке нет предложения nn.t. – Bick 10 April 2011 в 15:50
  • 2
    Теперь я думаю, что мой ответ - плохой ответ (см. Только мой ответ). Но если вы действительно хотите это сделать, я думаю, вам придется использовать цикл old-style for и начать с list.length () - 1. – Robin Green 10 April 2011 в 16:00
  • 3
    Чувак. не будьте так сильно на себе. По крайней мере, вы были первыми :-). – Bick 10 April 2011 в 16:04
  • 4
    В списках есть итератор списка, который имеет методы hasPrevious () и previous (). Вы можете установить начальную позицию итератора списка следующим образом: myList.listIterator (myList.size ()). Затем итерацию назад. Таким образом, это возможно, но поскольку вы предложили не очень хорошую идею в этом случае d (^_^) b – Edwin Dalorzo 10 April 2011 в 16:18

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

2
ответ дан Tomasz Stanczak 16 August 2018 в 00:47
поделиться

Что-то вроде этого:

  int pos = 0;  while (pos & lt; lst.size ()) {Foo foo = lst.get (pos);  if (hasToBeRemoved (foo)) {lst.remove (pos);  // не перемещать позицию} else {pos ++;  }}  
1
ответ дан Vladimir Dyuzhev 16 August 2018 в 00:47
поделиться
  • 1
    Это приятно, но нам очень сложно вычислить list.size. это возможно. – Bick 10 April 2011 в 15:52
  • 2
    Вычисление размера происходит во время удаления элемента, а удаление - это реальный удар производительности. Лучшая практика, как было сказано, собрать все элементы для удаления во временный массив и удалить их сразу. – Vladimir Dyuzhev 10 April 2011 в 16:26
Другие вопросы по тегам:

Похожие вопросы: