it-swarm.com.ru

Изменение объектов в потоке в Java8 во время итерации

В потоках Java8 мне разрешено изменять/обновлять объекты внутри? Например. List<User> users:

users.stream().forEach(u -> u.setProperty("value"))
51
Tejas Gokhale

Да, вы можете изменять состояние объектов в вашем потоке, но вам следует избегать изменения состояния source потока. Из невмешательства раздела документации пакета потоков мы можем прочитать, что:

Для большинства источников данных предотвращение помех означает, что источник данных вообще не изменен во время выполнения потокового конвейера. Заметным исключением из этого являются потоки, источники которых являются параллельными коллекциями, которые специально предназначены для обработки одновременных изменений. Источниками одновременных потоков являются те, чья Spliterator сообщает характеристику CONCURRENT.

Так что это нормально

List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));

но 

users.stream().forEach(u -> users.remove(u));

не является и может выдавать ConcurrentModificationException или другие исключения, такие как NPE:

List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .forEach(i -> list.remove(i));  //throws NullPointerException
61
Pshemo

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

new ArrayList<>(users).stream().forEach(u -> users.remove(u));
3
MNZ

Функциональный способ был бы следующим:

import static Java.util.stream.Collectors.toList;
import Java.util.Arrays;
import Java.util.List;
import Java.util.function.Predicate;

public class PredicateTestRun {

    public static void main(String[] args) {

        List<String> lines = Arrays.asList("a", "b", "c");
        System.out.println(lines); // [a, b, c]
        Predicate<? super String> predicate = value -> "b".equals(value);
        lines = lines.stream().filter(predicate.negate()).collect(toList());

        System.out.println(lines); // [a, c]
    }
}

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

1
Andreas M. Oberheim

Чтобы избавиться от ConcurrentModificationException Использовать CopyOnWriteArrayList  

0
black-coder

Вместо того, чтобы создавать странные вещи, вы можете просто filter(), а затем map() свой результат.

Это гораздо более читабельно и точно. Потоки сделают это всего за один цикл.

0
Nicolas Zozol

Это может быть немного поздно. Но вот одно из использования. Это, чтобы получить количество файлов. 

Создайте указатель на память (в данном случае новый объект) и измените свойство объекта. Поток Java 8 не позволяет изменять сам указатель и, следовательно, если вы объявите просто count как переменную и попытаетесь увеличить его в потоке, он никогда не сработает и вообще не вызовет исключение компилятора

Path path = Paths.get("/Users/XXXX/static/test.txt");



Count c = new Count();
            c.setCount(0);
            Files.lines(path).forEach(item -> {
                c.setCount(c.getCount()+1);
                System.out.println(item);});
            System.out.println("line count,"+c);

public static class Count{
        private int count;

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        @Override
        public String toString() {
            return "Count [count=" + count + "]";
        }



    }
0
Joey587

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

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

    final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());

    list.removeIf(number -> number % 2 == 0);
0
Jobin Joseph

Как упоминалось ранее - вы не можете изменять исходный список, но вы можете передавать, изменять и собирать элементы в новый список. Вот простой пример, как изменить строковый элемент.

public class StreamTest {

    @Test
    public void replaceInsideStream()  {
        List<String> list = Arrays.asList("test1", "test2_attr", "test3");
        List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
        System.out.println("Output: " + output); // Output: [test1, test2, test3]
    }
}
0
Vadim