streams - Modificar objetos dentro de la secuencia en Java8 mientras itera
stream java foreach (8)
En las transmisiones Java8, ¿puedo modificar / actualizar objetos dentro?
Por ej.
List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Como se mencionó anteriormente, no puede modificar la lista original, pero puede transmitir, modificar y recopilar elementos en una nueva lista. Aquí hay un ejemplo simple de cómo modificar el elemento de cadena.
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]
}
}
En lugar de crear cosas extrañas, puede simplemente
filter()
y luego
map()
su resultado.
Esto es mucho más legible y seguro. Las transmisiones lo harán en un solo bucle.
Esto podría ser un poco tarde. Pero aquí está uno de los usos. Esto para obtener el recuento de la cantidad de archivos.
Cree un puntero a la memoria (un nuevo obj en este caso) y modifique la propiedad del objeto. La secuencia Java 8 no permite modificar el puntero en sí mismo y, por lo tanto, si declara contar solo como una variable e intenta aumentar dentro de la secuencia, nunca funcionará y arrojará una excepción del compilador en primer 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 + "]";
}
}
La forma funcional sería:
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]
}
}
En esta solución, la lista original no se modifica, pero debe contener el resultado esperado en una nueva lista a la que se puede acceder con la misma variable que la anterior.
Para deshacerse de ConcurrentModificationException, use CopyOnWriteArrayList
Para realizar modificaciones estructurales en la fuente de la secuencia, como mencionó Pshemo en su respuesta, una solución es crear una nueva instancia de una
Collection
como
ArrayList
con los elementos dentro de su lista primaria;
iterar sobre la nueva lista y realizar las operaciones en la lista primaria.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
Puede utilizar
removeIf
para eliminar datos de una lista condicionalmente.
Por ejemplo: - Si desea eliminar todos los números pares de una lista, puede hacerlo de la siguiente manera.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
Sí, puede modificar el estado de los objetos dentro de su transmisión, pero debe evitar modificar el estado de la fuente de transmisión. Desde la sección de non-interference de la documentación del paquete de flujo podemos leer que:
Para la mayoría de las fuentes de datos, prevenir la interferencia significa garantizar que la fuente de datos no se modifique en absoluto durante la ejecución de la canalización de flujo. La notable excepción a esto son las secuencias cuyas fuentes son colecciones concurrentes, que están específicamente diseñadas para manejar modificaciones concurrentes. Las fuentes de flujo concurrentes son aquellas cuyo
Spliterator
informa la característicaCONCURRENT
.
Entonces esto está bien
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
pero
users.stream().forEach(u -> users.remove(u));
no es y puede lanzar
ConcurrentModificationException
u otras excepciones 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