java collections java-8 filtering java-stream

¿Pueden Java 8 Streams operar en un elemento de una colección y luego eliminarlo?



collections java-8 (8)

Después de la operación, no tienen ningún otro propósito y deben eliminarse del conjunto original. El código funciona bien, pero no puedo evitar la sensación de que hay una operación en Stream que podría hacer esto por mí, en una sola línea.

No puede eliminar elementos de la fuente de la secuencia con la secuencia. Del Javadoc :

La mayoría de las operaciones de flujo aceptan parámetros que describen el comportamiento especificado por el usuario ..... Para preservar el comportamiento correcto, estos parámetros de comportamiento:

  • no deben interferir (no modifican la fuente del flujo) ; y
  • en la mayoría de los casos debe ser sin estado (su resultado no debe depender de ningún estado que pueda cambiar durante la ejecución de la canalización de flujo).

Como casi todos, todavía estoy aprendiendo las complejidades (y amándolas) de la nueva API de Java 8 Streams. Tengo una pregunta sobre el uso de transmisiones. Proporcionaré un ejemplo simplificado.

Java Streams nos permite tomar una Collection y usar el método stream() para recibir una secuencia de todos sus elementos. Dentro de él, hay varios métodos útiles, como filter() , map() y forEach() , que nos permiten usar operaciones lambda en los contenidos.

Tengo un código que se parece a esto (simplificado):

set.stream().filter(item -> item.qualify()) .map(item -> (Qualifier)item).forEach(item -> item.operate()); set.removeIf(item -> item.qualify());

La idea es obtener un mapeo de todos los elementos del conjunto, que coincidan con un determinado calificador, y luego operar a través de ellos. Después de la operación, no tienen ningún otro propósito y deben eliminarse del conjunto original. El código funciona bien, pero no puedo evitar la sensación de que hay una operación en Stream que podría hacer esto por mí, en una sola línea.

Si está en los Javadocs, puedo pasarlo por alto.

¿Alguien más familiarizado con la API ve algo así?


En una línea no, pero tal vez podría hacer uso del partitioningBy recopilador:

Map<Boolean, Set<Item>> map = set.stream() .collect(partitioningBy(Item::qualify, toSet())); map.get(true).forEach(i -> ((Qualifier)i).operate()); set = map.get(false);

Podría ser más eficiente ya que evita iterar el conjunto dos veces, una para filtrar la secuencia y luego otra para eliminar los elementos correspondientes.

De lo contrario, creo que su enfoque es relativamente bueno.


Hay muchos enfoques. Si usa myList.remove (element) debe anular equals (). Lo que prefiero es:

allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));

Buena suerte y feliz codificación :)


Lo que realmente quieres hacer es particionar tu conjunto. Desafortunadamente, en Java 8 la partición solo es posible a través del método de "recopilación" del terminal. Terminas con algo como esto:

// test data set Set<Integer> set = ImmutableSet.of(1, 2, 3, 4, 5); // predicate separating even and odd numbers Predicate<Integer> evenNumber = n -> n % 2 == 0; // initial set partitioned by the predicate Map<Boolean, List<Integer>> partitioned = set.stream().collect(Collectors.partitioningBy(evenNumber)); // print even numbers partitioned.get(true).forEach(System.out::println); // do something else with the rest of the set (odd numbers) doSomethingElse(partitioned.get(false))

Actualizado:

Versión Scala del código anterior

val set = Set(1, 2, 3, 4, 5) val partitioned = set.partition(_ % 2 == 0) partitioned._1.foreach(println) doSomethingElse(partitioned._2)`


No, su implementación es probablemente la más simple. Puede hacer algo profundamente malo modificando el estado en el predicado removeIf , pero no lo haga. Por otro lado, podría ser razonable cambiar a una implementación imperativa basada en iterador, que en realidad podría ser más apropiada y eficiente para este caso de uso.


Puedes hacerlo así:

set.removeIf(item -> { if (!item.qualify()) return false; item.operate(); return true; });

Si item.operate() siempre devuelve true , puede hacerlo de manera muy sucinta.

set.removeIf(item -> item.qualify() && item.operate());

Sin embargo, no me gustan estos enfoques, ya que no está claro de inmediato qué está sucediendo. Personalmente, seguiría usando un bucle for y un Iterator para esto.

for (Iterator<Item> i = set.iterator(); i.hasNext();) { Item item = i.next(); if (item.qualify()) { item.operate(); i.remove(); } }


Veo la preocupación de Paul por la claridad al usar las transmisiones, que se indica en la respuesta principal. Quizás agregar variables explicativas aclare un poco las intenciones.

set.removeIf(item -> { boolean removeItem=item.qualify(); if (removeItem){ item.operate(); } return removeItem; });


si entiendo tu pregunta correctamente:

set = set.stream().filter(item -> { if (item.qualify()) { ((Qualifier) item).operate(); return false; } return true; }).collect(Collectors.toSet());