Подтвердить что ты не робот

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

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

users.stream().forEach(u -> u.setProperty("value"))
4b9b3361

Ответ 1

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

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

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

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

но

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

не является и может ConcurrentModificationException исключение 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

Ответ 2

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

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

Ответ 3

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

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]
    }
}

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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]
    }
}

Ответ 7

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

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

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

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

Ответ 8

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

Создайте указатель на память (в данном случае новый объект) и измените свойство объекта. Поток 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 + "]";
        }



    }