Em fluxos Java8, tenho permissão para modificar / atualizar objetos dentro? Por exemplo. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
fonte
Em fluxos Java8, tenho permissão para modificar / atualizar objetos dentro? Por exemplo. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Sim, você pode modificar o estado dos objetos dentro do seu fluxo, mas na maioria das vezes você deve evitar modificar o estado da fonte do fluxo. Na seção de não interferência da documentação do pacote de fluxo, podemos ler que:
Para a maioria das fontes de dados, evitar interferência significa garantir que a fonte de dados não seja modificada durante a execução do pipeline de fluxo. A exceção notável a isso são os fluxos cujas origens são coleções simultâneas, que são projetadas especificamente para lidar com a modificação simultânea. Fontes de fluxo simultâneo são aquelas cujos
Spliterator
relatórios aCONCURRENT
característica.
Então está tudo bem
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
mas isso na maioria dos casos não é
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
e pode lançar ConcurrentModificationException
ou até mesmo outras exceções inesperadas como 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
Iterator
que impede a modificação da lista (remover / adicionar novos elementos) durante a iteração e ambos os fluxos e os loops for-each estão usando-a. Você pode tentar usar um loop simples como ofor(int i=0; ..; ..)
qual não tem esse problema (mas não irá impedi-lo quando outro thread modificar sua lista). Você também pode usar métodos comolist.removeAll(Collection)
,list.removeIf(Predicate)
. Além disso, ajava.util.Collections
classe tem poucos métodos que podem ser úteis comoaddAll(CollectionOfNewElements,list)
.filter()
A forma funcional seria imho:
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] } }
Nesta solução, a lista original não é modificada, mas deve conter seu resultado esperado em uma nova lista que é acessível sob a mesma variável da antiga
fonte
Para fazer modificações estruturais na origem do fluxo, como Pshemo mencionou em sua resposta, uma solução é criar uma nova instância de um
Collection
similarArrayList
com os itens dentro de sua lista primária; itere sobre a nova lista e faça as operações na lista primária.new ArrayList<>(users).stream().forEach(u -> users.remove(u));
fonte
Para se livrar de ConcurrentModificationException, use CopyOnWriteArrayList
fonte
List<Something>
- você não tem ideia se é um CopyOnWriteArrayList. Portanto, este é mais um comentário medíocre, mas não uma resposta real.Em vez de criar coisas estranhas, você pode apenas
filter()
e entãomap()
seu resultado.Isso é muito mais legível e seguro. Streams fará isso em apenas um loop.
fonte
Sim, você pode modificar ou atualizar os valores dos objetos na lista no seu caso da mesma forma:
users.stream().forEach(u -> u.setProperty("some_value"))
No entanto, a instrução acima fará atualizações nos objetos de origem. O que pode não ser aceitável na maioria dos casos.
Felizmente, temos outra maneira como:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
O que retorna uma lista atualizada, sem prejudicar a antiga.
fonte
Como foi mencionado antes - você não pode modificar a lista original, mas pode transmitir, modificar e coletar itens em uma nova lista. Aqui está um exemplo simples de como modificar o elemento string.
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] } }
fonte
Você pode usar
removeIf
para remover dados de uma lista condicionalmente.Ex: - Se você deseja remover todos os números pares de uma lista, pode fazer da seguinte maneira.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList()); list.removeIf(number -> number % 2 == 0);
fonte
Isso pode ser um pouco tarde. Mas aqui está um dos usos. Isso para obter a contagem do número de arquivos.
Crie um ponteiro para a memória (um novo obj neste caso) e modifique a propriedade do objeto. O fluxo Java 8 não permite modificar o próprio ponteiro e, portanto, se você declarar apenas contar como uma variável e tentar incrementar dentro do fluxo, ele nunca funcionará e lançará uma exceção do compilador em primeiro lugar
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 + "]"; } }
fonte