В потоках Java8 мне разрешено изменять/обновлять объекты внутри?
Напр. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
В потоках Java8 мне разрешено изменять/обновлять объекты внутри?
Напр. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Да, вы можете изменить состояние объектов в вашем потоке, но вам следует избегать изменения состояния источника потока. Из раздела невмешательства документации пакета потоков мы можем прочитать, что:
Для большинства источников данных предотвращение помех означает, что источник данных вообще не изменяется во время выполнения потокового конвейера. Заметным исключением из этого являются потоки, источники которых являются параллельными коллекциями, которые специально предназначены для обработки одновременных изменений. Источниками параллельного потока являются те, чей
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
Чтобы выполнить структурную модификацию источника потока, как указано в его ответе в Pshemo, одним из решений является создание нового экземпляра Collection
like ArrayList
с элементами внутри вашего основного списка; перебирать новый список и выполнять операции в основном списке.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
Функциональный способ был бы следующим:
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]
}
}
В этом решении исходный список не изменяется, но должен содержать ожидаемый результат в новом списке, который доступен под той же переменной, что и старый.
Чтобы избавиться от ConcurrentModificationException Используйте CopyOnWriteArrayList
Вместо того, чтобы создавать странные вещи, вы можете просто filter()
и затем map()
свой результат.
Это гораздо более читабельно и точно. Потоки сделают это всего за один цикл.
Как упоминалось ранее - вы не можете изменять исходный список, но вы можете передавать, изменять и собирать элементы в новый список. Вот простой пример, как изменить строковый элемент.
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]
}
}
Вы можете использовать команду removeIf
для removeIf
удаления данных из списка.
Например: - Если вы хотите удалить все четные числа из списка, вы можете сделать это следующим образом.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
Это может быть немного поздно. Но вот одно из использования. Это, чтобы получить количество файлов.
Создайте указатель на память (в данном случае новый объект) и измените свойство объекта. Поток 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 + "]";
}
}