4 votes

Filtrer le flux en utilisant la corrélation entre les éléments du flux

Disons qu'il y a un Person qui ressemble à ceci :

public class Person {
    private int id;
    private String discriminator;
    // some other fields plus getters/setters
}

J'ai maintenant un Stream de Person et ce flux peut contenir plusieurs éléments Person qui ont les mêmes id mais différents discriminator c'est-à-dire [Person{“id”: 1, “discriminator”: “A”}, Person{“id”: 1, “discriminator”: “B”}, Person{“id”: 2, “discriminator”: “A”}, ...]

Ce que je voudrais faire, c'est filtrer tous les Person avec un certain identifiant s'il y a au moins une instance de Person avec cet identifiant et qui possède une valeur particulière de discriminant. Ainsi, en continuant avec l'exemple ci-dessus, le filtrage par la valeur du discriminateur "A" donnerait une collection vide (après l'opération de réduction, bien sûr) et le filtrage par la valeur du discriminateur "B" donnerait une collection ne contenant aucun Person dont l'identifiant est égal à 1.

Je sais que je peux réduire le flux en utilisant groupingBy les collecteurs et les éléments de groupe par Person.id puis supprimez le mappage de l'image résultante Map si la liste mappée contient un Person avec la valeur de discriminant spécifiée, mais je me demande s'il n'y a pas un moyen plus simple d'obtenir le même résultat ?

3voto

Eugene Points 6271

Si j'ai bien compris votre problème, vous devez d'abord trouver tous les ID qui correspondraient à un discriminant :

Set<Integer> ids = persons.stream()
       .filter(p -> "A".equalsIgnoreCase(p.getDiscriminator()))
       .map(Person::getId)
       .collect(Collectors.toSet())

Ensuite, il faut supprimer les entrées qui correspondent à ces données :

persons.removeIf(x -> ids.contains(x.getId()))

1voto

Lino Points 13360

La réponse d'Eugenes convient parfaitement, mais je préfère personnellement une déclaration unique. J'ai donc repris son code et je l'ai rassemblé en une seule opération. Ce qui ressemble à ceci :

final List<Person> result = persons.stream()
    .filter(p -> "B".equalsIgnoreCase(p.getDiscriminator()))
    .map(Person::getId)
    .collect(
        () -> new ArrayList<>(persons),
        ( list, id ) -> list.removeIf(p -> p.getId() == id),
        ( a, b ) -> {throw new UnsupportedOperationException();}
    );

Il est probablement nécessaire de mentionner que la copie de persons est nécessaire, sinon le flux sera corrompu et les rencontres null valeurs.

Note de côté : Cette version génère actuellement un UnsupportedOperationException lors de l'utilisation en parallèle.

1voto

jigga Points 120

Je présente donc ci-dessous la solution que j'ai trouvée. Tout d'abord, je regroupe la collection/le flux d'entrée Person par l'attribut Person.id, puis je passe en revue les entrées de la carte et je filtre celles dont au moins une valeur correspond à un discriminant donné.

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(
            new Person(1, "A"),
            new Person(1, "B"),
            new Person(1, "C"),
            new Person(2, "B"),
            new Person(2, "C"),
            new Person(3, "A"),
            new Person(3, "C"),
            new Person(4, "B")
        );

        System.out.println(persons);
        System.out.println(filterByDiscriminator(persons, "A"));
        System.out.println(filterByDiscriminator(persons, "B"));
        System.out.println(filterByDiscriminator(persons, "C"));
    }

    private static List<Person> filterByDiscriminator(List<Person> input, String discriminator) {
        return input.stream()
            .collect(Collectors.groupingBy(Person::getId))
            .entrySet().stream()
            .filter(entry -> entry.getValue().stream().noneMatch(person -> person.getDiscriminator().equals(discriminator)))
            .flatMap(entry -> entry.getValue().stream())
            .collect(Collectors.toList());
    }

}

class Person {

    private final Integer id;
    private final String discriminator;

    public Person(Integer id, String discriminator) {
        Objects.requireNonNull(id);
        Objects.requireNonNull(discriminator);
        this.id = id;
        this.discriminator = discriminator;
    }

    public Integer getId() {
        return id;
    }

    public String getDiscriminator() {
        return discriminator;
    }

    @Override
    public String toString() {
        return String.format("%s{\"id\": %d, \"discriminator\": \"%s\"}", getClass().getSimpleName(), id, discriminator);
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X