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


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

Java Streams nos permite tomar un Collection, y usar el método stream() en él para recibir un flujo de todos sus elementos. Dentro de él, hay una serie de métodos útiles, tales como filter(), map(), y forEach(), que nos permiten usar operaciones lambda sobre el contenido.

Tengo un código que se ve algo como 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 una asignación de todos los elementos del conjunto, que coinciden con un calificador determinado, y luego operar a través de ellos. Después de la operación, no sirven para ningún otro propósito, y deben eliminarse del conjunto original. El código funciona bien, pero no puedo sacudir 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, puede que lo esté pasando por alto.

¿alguien más familiarizado con la API ver algo como eso?

Author: Stuart Marks, 2015-05-05

8 answers

Puedes hacerlo así:

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

Si item.operate() siempre devuelve true puedes hacerlo muy sucintamente.

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

Sin embargo, no me gustan estos enfoques, ya que no está claro de inmediato lo que está pasando. Personalmente, continuarí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();
    }
}
 96
Author: Paul Boddington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-04 23:24:43

En una línea no, pero tal vez usted podría hacer uso de la partitioningBy colector:

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 el flujo y luego otra para eliminar los elementos correspondientes.

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

 4
Author: Alexis C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-04 23:03:56

Lo que realmente quiere hacer es particionar su conjunto. Desafortunadamente, en Java 8 el particionamiento solo es posible a través del método terminal "collect". 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)`
 2
Author: Dmitriy Yefremov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-05 03:50:52

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

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

Buena suerte y feliz codificación:)

 2
Author: panayot_kulchev_bg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-12 08:39:40

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

 1
Author: Louis Wasserman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-04 22:47:32

Si entiendo su pregunta correctamente:

set = set.stream().filter(item -> {
    if (item.qualify()) {
        ((Qualifier) item).operate();
        return false;
    }
    return true;
}).collect(Collectors.toSet());
 1
Author: user_3380739,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-05 23:09:39

Después de la operación, no sirven para otro propósito, y deben eliminarse del conjunto original. El código funciona bien, pero no puedo quitarme 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. Desde el 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:

  • debe ser no interferente (no modifican la fuente de flujo) ; y
  • en la mayoría de los casos debe ser apátrida (su resultado no debe depender de ningún estado que pueda cambiar durante la ejecución de la canalización de flujo).
 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-21 17:49:15

Veo la preocupación de la claridad de Pablo al usar las corrientes, expresada en la respuesta superior. Tal vez agregar la variable explicativa aclara un poco las intenciones.

set.removeIf(item -> {
  boolean removeItem=item.qualify();
  if (removeItem){
    item.operate();
  }
  return removeItem;
});
 0
Author: vacant78,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-09 08:58:22