web-dev-qa-db-fra.com

Java 8 lambda obtenir et supprimer un élément de la liste

Étant donné une liste d'éléments, je veux obtenir l'élément avec une propriété donnée et / le supprimer de la liste. La meilleure solution que j'ai trouvée est:

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

Est-il possible de combiner obtenir et supprimer dans une expression lambda?

43
Marco Stramezzi

Supprimer un élément de la liste  

objectA.removeIf (x -> conditions);

exemple: 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);

SORTIE: A B C

74
uma shankar

Bien que le thread soit assez ancien, on pense toujours à fournir une solution - en utilisant Java8.

Utilisez la fonction removeIf. La complexité du temps est O(n)

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

Référence de l'API: removeIf docs

Hypothèse: producersProcedureActive est une List

NOTE: Avec cette approche, vous ne pourrez plus récupérer l'élément supprimé.

17
asifsid88

Pensez à utiliser les itérateurs Java Java pour effectuer la tâche:

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;
}

Avantages :

  1. C'est clair et évident.
  2. Il ne traverse qu'une seule fois et jusqu'à l'élément correspondant.
  3. Vous pouvez le faire sur n'importe quelle variable Iterable même sans stream() support (au moins ceux qui implémentent remove() sur leur itérateur)}.

Désavantages :

  1. Vous ne pouvez pas le faire à la place en tant qu’expression unique (méthode auxiliaire ou variable requise)

En ce qui concerne la

Est-il possible de combiner obtenir et supprimer dans une expression lambda?

d’autres réponses montrent clairement que c’est possible, mais vous devez être conscient de

  1. La recherche et la suppression peuvent parcourir la liste deux fois
  2. ConcurrentModificationException peut être levé lors de la suppression d'un élément de la liste en cours d'itération
15
Vasily Liaskovsky

La solution directe serait d’invoquer ifPresent(consumer) sur l’option facultative renvoyée par findFirst() . Ce consommateur sera appelé lorsque l'option facultative n'est pas vide. L'avantage est également qu'il ne lève pas d'exception si l'opération de recherche a renvoyé une valeur optionnelle vide, comme le ferait votre code actuel. au lieu de cela, rien ne se passera.

Si vous souhaitez renvoyer la valeur supprimée, vous pouvez map la Optional au résultat de l'appel de remove:

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

Mais notez que l'opération remove(Object) parcourra à nouveau la liste pour rechercher l'élément à supprimer. Si vous avez une liste avec un accès aléatoire, telle qu'une ArrayList, il serait préférable de créer un flux sur les index de la liste et de trouver le premier index correspondant au prédicat:

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

Avec cette solution, l'opération remove(int) opère directement sur l'index.

9
Tunaki

Use peut utiliser le filtre de Java 8 et créer une autre liste si vous ne souhaitez pas modifier l'ancienne liste:

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

Je suis sûr que ce sera une réponse impopulaire, mais ça marche ...

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

p[0] détiendra l'élément trouvé ou sera null.

Le "truc" consiste ici à contourner le problème "effectivement final" en utilisant une référence array / qui est effectivement finale, mais en définissant son premier élément.

3
Bohemian

Avec Collections Eclipse vous pouvez utiliser detectIndex avec remove(int) sur n’importe quel fichier 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 vous utilisez le type MutableList des collections Eclipse, vous pouvez appeler la méthode detectIndex directement dans la liste.

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);

Remarque: je suis un partisan des collections Eclipse

2
Donald Raab

Comme d'autres l'ont suggéré, cela pourrait être un cas d'utilisation des boucles et des iterables. À mon avis, c'est l'approche la plus simple. Si vous souhaitez modifier la liste sur place, elle ne peut de toute façon pas être considérée comme une "vraie" programmation fonctionnelle. Mais vous pouvez utiliser Collectors.partitioningBy() pour obtenir une nouvelle liste avec des éléments satisfaisant votre condition et une nouvelle liste de ceux qui ne le sont pas. Bien sûr, avec cette approche, si vous avez plusieurs éléments satisfaisant la condition, tous ceux-ci seront dans cette liste et pas seulement le premier.

1
user140547

la tâche est la suivante: get ✶ and ✶ retirer un élément de la liste

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
  } ) );
0
Kaplan

La logique ci-dessous est la solution sans modifier la liste d'origine

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"]
0
KimchiMan

En combinant mon idée initiale et vos réponses, j'ai atteint ce qui semble être la solutionà ma propre question:

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;
}

Il permet de supprimer l'objet à l'aide de remove(int) qui ne repasse pas dans la liste .__ (comme suggéré par @Tunaki) et permet de renvoyer l'objet supprimé à L'appelant de fonction.

J'ai lu vos réponses qui me suggèrent de choisir des méthodes sûres comme ifPresent au lieu de get mais je ne trouve pas le moyen de les utiliser dans ce scénario.

Existe-t-il un inconvénient important dans ce type de solution? 

Editez suivant les conseils de @Holger

Cela devrait être la fonction dont j'avais besoin

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; 
            });
}
0
Marco Stramezzi

Lorsque nous voulons importer plusieurs éléments d'une liste dans une nouvelle liste (filtrer à l'aide d'un prédicat) et les supprimer de la liste existante , je n'ai pas trouvé une bonne réponse n'importe où.

Voici comment nous pouvons le faire en utilisant Java Partitionnement de l'API en 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 cette façon, vous supprimez efficacement les éléments filtrés de la liste d'origine et les ajoutez à une nouvelle liste.

Reportez-vous à la section 5.2. Collectors.partitioningBy section de cet article .

0