una suma streams remove procesamiento parte metodo listas lista expresiones eliminar elementos ejemplos datos con java lambda java-8 java-stream

suma - Java 8 lambda obtener y eliminar elemento de la lista



streams lambda java (12)

Dada una lista de elementos, quiero obtener el elemento con una propiedad dada y eliminarlo de la lista. La mejor solución que encontré es:

ProducerDTO p = producersProcedureActive .stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .get(); producersProcedureActive.remove(p);

¿Es posible combinar get y remove en una expresión lambda?


Aunque el subproceso es bastante antiguo, se pensó que proporcionaría una solución, utilizando Java8 .

Haga uso de la función removeIf . La complejidad del tiempo es O(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));

Referencia de API: removeIf docs

Supuesto: producersProcedureActive es una List

NOTA: con este enfoque, no podrá obtener el elemento eliminado.


Combinando mi idea inicial y sus respuestas, llegué a lo que parece ser la solución a mi propia pregunta:

public ProducerDTO findAndRemove(String pod) { ProducerDTO p = null; try { p = IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int)i)) .get(); logger.debug(p); } catch (NoSuchElementException e) { logger.error("No producer found with POD [" + pod + "]"); } return p; }

Permite eliminar el objeto utilizando remove(int) que no recorre nuevamente la lista (como lo sugiere @Tunaki) y permite devolver el objeto eliminado a la función de llamada.

Leí sus respuestas que me sugieren elegir métodos seguros como ifPresent lugar de get pero no encuentro la manera de usarlos en este escenario.

¿Hay algún inconveniente importante en este tipo de solución?

Editar siguiendo los consejos de @Holger

Esta debería ser la función que necesitaba

public ProducerDTO findAndRemove(String pod) { return IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; }); }


Como otros han sugerido, este podría ser un caso de uso para bucles e iterables. En mi opinión, este es el enfoque más simple. Si desea modificar la lista in situ, no puede considerarse programación funcional "real" de todos modos. Pero podría usar Collectors.partitioningBy() para obtener una nueva lista con elementos que satisfagan su condición, y una nueva lista de los que no. Por supuesto, con este enfoque, si tiene múltiples elementos que satisfacen la condición, todos estarán en esa lista y no solo el primero.


Con Eclipse Collections puede usar detectIndex junto con remove(int) en cualquier java.util.List.

List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5); int index = Iterate.detectIndex(integers, i -> i > 2); if (index > -1) { integers.remove(index); } Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

Si usa el tipo MutableList de Eclipse Collections, puede llamar al método detectIndex directamente en la lista.

MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5); int index = integers.detectIndex(i -> i > 2); if (index > -1) { integers.remove(index); } Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

Nota: Soy un committer para Eclipse Collections


Considere usar iteradores vanilla java para realizar la tarea:

public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) { T value = null; for (Iterator<? extends T> it = collection.iterator(); it.hasNext();) if (test.test(value = it.next())) { it.remove(); return value; } return null; }

Ventajas :

  1. Es claro y obvio.
  2. Atraviesa solo una vez y solo hasta el elemento correspondiente.
  3. Puede hacerlo en cualquier Iterable incluso sin el soporte de stream() (al menos aquellos que implementan remove() en su iterador) .

Desventajas

  1. No puede hacerlo en su lugar como una sola expresión (se requiere método auxiliar o variable)

En cuanto a

¿Es posible combinar get y remove en una expresión lambda?

otras respuestas muestran claramente que es posible, pero debe tener en cuenta

  1. La búsqueda y eliminación pueden atravesar la lista dos veces
  2. ConcurrentModificationException puede ConcurrentModificationException al eliminar un elemento de la lista que se está iterando

Cuando queremos obtener múltiples elementos de una Lista en una nueva lista (filtrar usando un predicado) y eliminarlos de la lista existente , no pude encontrar una respuesta adecuada en ningún lado.

Aquí es cómo podemos hacerlo utilizando la partición de API Java Streaming.

Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive .stream() .collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod))); // get two new lists List<ProducerDTO> matching = classifiedElements.get(true); List<ProducerDTO> nonMatching = classifiedElements.get(false); // OR get non-matching elements to the existing list producersProcedureActive = classifiedElements.get(false);

De esta manera, elimina efectivamente los elementos filtrados de la lista original y los agrega a una nueva lista.

Consulte el 5.2. Colectores.particiónPor sección de este artículo .


Estoy seguro de que esta será una respuesta impopular, pero funciona ...

ProducerDTO[] p = new ProducerDTO[1]; producersProcedureActive .stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}

p[0] mantendrá el elemento encontrado o será nulo.

El "truco" aquí es eludir el problema "efectivamente final" mediante el uso de una referencia de matriz que es efectivamente final, pero estableciendo su primer elemento.


La siguiente lógica es la solución sin modificar la lista original.

List<String> str1 = new ArrayList<String>(); str1.add("A"); str1.add("B"); str1.add("C"); str1.add("D"); List<String> str2 = new ArrayList<String>(); str2.add("D"); str2.add("E"); List<String> str3 = str1.stream() .filter(item -> !str2.contains(item)) .collect(Collectors.toList()); str1 // ["A", "B", "C", "D"] str2 // ["D", "E"] str3 // ["A", "B", "C"]


La solución directa sería invocar ifPresent(consumer) en el Opcional devuelto por findFirst() . Este consumidor será invocado cuando lo opcional no esté vacío. El beneficio también es que no arrojará una excepción si la operación de búsqueda devuelve un opcional vacío, como lo haría su código actual; en cambio, no pasará nada.

Si desea devolver el valor eliminado, puede map el Optional al resultado de llamar a remove :

producersProcedureActive.stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .map(p -> { producersProcedureActive.remove(p); return p; });

Pero tenga en cuenta que la operación remove(Object) volverá a recorrer la lista para encontrar el elemento a eliminar. Si tiene una lista con acceso aleatorio, como una ArrayList , sería mejor hacer un Stream sobre los índices de la lista y encontrar el primer índice que coincida con el predicado:

IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int) i));

Con esta solución, la operación remove(int) opera directamente en el índice.


Use puede usar el filtro de Java 8 y crear otra lista si no desea cambiar la lista anterior:

List<ProducerDTO> result = producersProcedureActive .stream() .filter(producer -> producer.getPod().equals(pod)) .collect(Collectors.toList());


la tarea es: obtener ✶ y ✶ eliminar elemento de la lista

p.stream().collect( Collectors.collectingAndThen( Collector.of( ArrayDeque::new, (a, producer) -> { if( producer.getPod().equals( pod ) ) a.addLast( producer ); }, (a1, a2) -> { return( a1 ); }, rslt -> rslt.pollFirst() ), (e) -> { if( e != null ) p.remove( e ); // remove return( e ); // get } ) );


Eliminar elemento de la lista

objectA.removeIf (x -> condiciones);

por ejemplo: objectA.removeIf (x -> blockWorkerIds.contains (x));

List<String> str1 = new ArrayList<String>(); str1.add("A"); str1.add("B"); str1.add("C"); str1.add("D"); List<String> str2 = new ArrayList<String>(); str2.add("D"); str2.add("E"); str1.removeIf(x -> str2.contains(x)); str1.forEach(System.out::println);

SALIDA: A B C