2015-03-23 4 views
7

Давайте посмотрим на этот пример:Почему iterator.forEachRemaining не удаляет элемент в потребительской лямбда?

public class ListIteratorTest { 
    public static void main(String[] args) { 
     List<String> list = new ArrayList<>(); 
     list.add("element1"); 
     list.add("element2"); 
     list.add("element3"); 
     list.add("element4"); 

     ListIterator<String> iterator = list.listIterator(); 
    } 
} 

И сейчас, это работает отлично:

// prints elements out, and then appropriately removes one after another 
    while (iterator.hasNext()){ 
     System.out.println(iterator.next()); 
     iterator.remove(); 
    } 

в то время как это бросает IllegalStateException:

 // throws IllegalStateException, why? 
     iterator.forEachRemaining(n -> { 
      System.out.println(n); 
      iterator.remove(); 
     }); 

Мой вопрос короток: почему ?

+0

Лямбда-выражения (и, конечно же, ссылки на методы) также имеют большое преимущество в создании гораздо более читаемого или чистого кода. Я стараюсь использовать их везде, где я получаю реальную пользу - с точки зрения удобочитаемости - от них. Ваш пример лямбда не читается или менее читабельен, чем пример, который не использует выражение лямбда. Фактически, он имеет такое же количество кодовых строк. Так почему вы чувствуете, что вы должны (или должны) использовать лямбда здесь? Фактически ... вы обнаружили это ... они делают ваш код несколько сложным, так как вы не должны использовать итератор внутри лямбды. – Seelenvirtuose

+0

@Seelenvirtuose Я все еще участвую в обучении Java8, поэтому я стараюсь использовать lambdas и т. Д. Везде, где только могу. Я просто подумал, что если это возможно, и я хотел бы проверить это - это дало неожиданные результаты, и я не знал, почему так спросил здесь; но спасибо за объяснение того, что на самом деле сделано для lambas - для простоты, где это возможно, – azalut

ответ

12

Обновлено благодаря @studro. См. Его комментарий ниже.

В API documentation состоянии:

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

Похоже, что часть «неуказанного поведения» также применяется во время этой внутренней итерации.

Конечно, документация для forEachRemaining утверждает, что поведение эквивалентно

while (hasNext()) 
    action.accept(next()); 

и если action::accept сделал фактически вызов iterator.remove() выше фрагмент кода не должен бросать никаких исключений (если remove операция поддерживается). Это может быть ошибка в документации.

+0

Итак, можно ли перебирать список через lambdas (например, iterator.forEachRemaining()) и удалять элемент после его работы? – azalut

+3

Несомненно. Вы можете хранить элементы, подлежащие удалению, в отдельный список, 'toDelete', а после завершения forEachRemaining вы можете использовать' list.removeAll (toDelete) '. Это, однако, не идиоматический способ решения проблемы в Java 8. Вероятно, вы хотите использовать 'stream.filter'. – aioobe

+4

... или просто 'list.removeIf (...)', если вы хотите изменить список на идиотском курсе java 8. –