loops множества - Вызов удаления в цикле foreach в Java





элементов из (10)


for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

Вы клонируете names списков и итерации через клон, пока вы удаляете из исходного списка. Немного чище, чем верхний ответ.

В Java разрешено ли вызывать удаление в коллекции при повторении через коллекцию с использованием цикла foreach? Например:

List<String> names = ....
for (String name : names) {
   // Do something
   names.remove(name).
}

В качестве дополнения, законно ли удалять элементы, которые еще не были повторены? Например,

//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
    // Do something
    while (names.remove(name));
}



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

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

Вместо этого напишите свой код:

List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {

    String name = it.next();
    // Do something
    it.remove();
}

Обратите внимание, что код вызывает Iterator.remove , а не List.remove .

Приложение:

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




Лучше использовать Итератор, если вы хотите удалить элемент из списка

потому что исходный код удаления

if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
elementData[--size] = null;

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




Убедитесь, что это не запах кода. Можно ли отменить логику и быть «включенным», а не «эксклюзивным»?

List<String> names = ....
List<String> reducedNames = ....
for (String name : names) {
   // Do something
   if (conditionToIncludeMet)
       reducedNames.add(name);
}
return reducedNames;

Ситуация, которая привела меня к этой странице, включала старый код, который зацикливался на List с помощью indecies для удаления элементов из списка. Я хотел реорганизовать его для использования стиля foreach.

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

List<Service> services = ...
for (int i=0; i<services.size(); i++) {
    if (!isServicePermitted(user, services.get(i)))
         services.remove(i);
}

Чтобы отменить это, а не использовать удаление:

List<Service> services = ...
List<Service> permittedServices = ...
for (Service service:services) {
    if (isServicePermitted(user, service))
         permittedServices.add(service);
}
return permittedServices;

Когда «удалить» будет предпочтительнее? Одним из соображений является наличие большого списка или дорогостоящего «добавления» в сочетании с несколькими удаленными по сравнению с размером списка. Возможно, было бы более эффективно делать только несколько удалений, а не добавлять много. Но в моем случае ситуация не заслуживала такой оптимизации.




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




использование

.remove () Интератора или

использование

CopyOnWriteArrayList




Чтобы безопасно удалить из коллекции, итерации по ней, вы должны использовать Iterator.

Например:

List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
   String s = i.next(); // must be called before you can call i.remove()
   // Do something
   i.remove();
}

Из документации Java :

Итераторы, возвращаемые методами итератора этого класса и listIterator, работают с ошибкой: если список структурно модифицируется в любое время после создания итератора, любым способом, кроме как с помощью собственных методов удаления или добавления итератора, итератор будет вызывать исключение ConcurrentModificationException. Таким образом, перед лицом одновременной модификации итератор быстро и чисто, а не рискует произвольным, недетерминированным поведением в неопределенное время в будущем.

Возможно, для многих новичков непонятно, что итерация по списку с использованием конструкторов for / foreach неявно создает итератор, который обязательно недоступен. Эту информацию можно найти here




Я не знал об итераторах, но вот что я делал до сегодняшнего дня, чтобы удалить элементы из списка внутри цикла:

List<String> names = .... 
for (i=names.size()-1;i>=0;i--) {    
    // Do something    
    names.remove(i);
} 

Это всегда работает и может использоваться на других языках или структурах, не поддерживающих итераторы.




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

List<String> names = ....

// introduce a separate list to hold removing items
List<String> toRemove= new ArrayList<String>();

for (String name : names) {
   // Do something: perform conditional checks
   toRemove.add(name);
}    
names.removeAll(toRemove);

// now names list holds expected values



Вот еще одна вариация метода, подобная Bjorn's (apphacker), которая позволяет вам назначать значение переменной внутри функции, а не передавать ее как параметр, который может быть более ясным:

for (var i = 0; i < 3; i++) {
    funcs[i] = (function() {
        var index = i;
        return function() {
            console.log("My value: " + index);
        }
    })();
}

Обратите внимание, что любой метод, который вы используете, index переменная становится своего рода статической переменной, связанной с возвращенной копией внутренней функции. Т.е., изменения в его значении сохраняются между вызовами. Это может быть очень удобно.





java loops iterator foreach