web-dev-qa-db-fra.com

Extraire les objets en double d'une liste en Java 8

Ce code supprime les doublons de la liste d'origine, mais je veux extraire les doublons de la liste d'origine -> ne les supprime pas (ce nom de package fait simplement partie d'un autre projet):

Donné:

une personne pojo:

package at.mavila.learn.kafka.kafkaexercises;

import org.Apache.commons.lang3.builder.ToStringBuilder;

public class Person {

private final Long id;
private final String firstName;
private final String secondName;


private Person(final Builder builder) {
    this.id = builder.id;
    this.firstName = builder.firstName;
    this.secondName = builder.secondName;
}


public Long getId() {
    return id;
}

public String getFirstName() {
    return firstName;
}

public String getSecondName() {
    return secondName;
}

public static class Builder {

    private Long id;
    private String firstName;
    private String secondName;

    public Builder id(final Long builder) {
        this.id = builder;
        return this;
    }

    public Builder firstName(final String first) {
        this.firstName = first;
        return this;
    }

    public Builder secondName(final String second) {
        this.secondName = second;
        return this;
    }

    public Person build() {
        return new Person(this);
    }


}

@Override
public String toString() {
    return new ToStringBuilder(this)
            .append("id", id)
            .append("firstName", firstName)
            .append("secondName", secondName)
            .toString();
}
}

Code d'extraction de duplication.

Remarquez ici nous filtrons l'id et le prénom pour récupérer une nouvelle liste, j'ai vu ce code ailleurs, pas le mien:

package at.mavila.learn.kafka.kafkaexercises;

import Java.util.List;
import Java.util.Map;
import Java.util.Objects;
import Java.util.concurrent.ConcurrentHashMap;
import Java.util.function.Function;
import Java.util.function.Predicate;
import Java.util.stream.Collectors;

import static Java.util.Objects.isNull;

public final class DuplicatePersonFilter {


private DuplicatePersonFilter() {
    //No instances of this class
}

public static List<Person> getDuplicates(final List<Person> personList) {

   return personList
           .stream()
           .filter(duplicateByKey(Person::getId))
           .filter(duplicateByKey(Person::getFirstName))
           .collect(Collectors.toList());

}

private static <T> Predicate<T> duplicateByKey(final Function<? super T, Object> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    return t -> isNull(seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE));

}

}

Le code de test. Si vous exécutez ce test, vous obtiendrez [alex, lolita, elpidio, romualdo].

Je m'attendrais à obtenir à la place [romualdo, otroRomualdo] en tant que doublons extraits, en fonction de l'id et du firstName:

package at.mavila.learn.kafka.kafkaexercises;


import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import Java.util.ArrayList;
import Java.util.List;

import static org.junit.Assert.*;

public class DuplicatePersonFilterTest {

private static final Logger LOGGER = LoggerFactory.getLogger(DuplicatePersonFilterTest.class);



@Test
public void testList(){

    Person alex = new Person.Builder().id(1L).firstName("alex").secondName("salgado").build();
    Person lolita = new Person.Builder().id(2L).firstName("lolita").secondName("llanero").build();
    Person elpidio = new Person.Builder().id(3L).firstName("elpidio").secondName("ramirez").build();
    Person romualdo = new Person.Builder().id(4L).firstName("romualdo").secondName("gomez").build();
    Person otroRomualdo = new Person.Builder().id(4L).firstName("romualdo").secondName("perez").build();


    List<Person> personList = new ArrayList<>();

    personList.add(alex);
    personList.add(lolita);
    personList.add(elpidio);
    personList.add(romualdo);
    personList.add(otroRomualdo);

    final List<Person> duplicates = DuplicatePersonFilter.getDuplicates(personList);

    LOGGER.info("Duplicates: {}",duplicates);

}

}

Dans mon travail, j'ai pu obtenir le résultat souhaité en utilisant Comparator en utilisant TreeMap et ArrayList, mais il s'agissait de créer une liste, puis de la filtrer, de passer à nouveau le filtre à une liste nouvellement créée.

Quelqu'un a-t-il une meilleure idée de la façon d'extraire les doublons?, Ne les supprime pas.

Merci d'avance. 

Mettre à jour :

Merci à tous pour vos réponses

Pour supprimer le duplicata en utilisant la même approche avec uniqueAttributes:

 public static List<Person> removeDuplicates(final List<Person> personList) {

    return personList.stream().collect(Collectors
            .collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(
                    PersonListFilters::uniqueAttributes))),
                    ArrayList::new));

}

 private static String uniqueAttributes(Person person){

    if(Objects.isNull(person)){
        return StringUtils.EMPTY;
    }



    return (person.getId()) + (person.getFirstName()) ;
}

Pour identifier les doublons, aucune méthode à ma connaissance n'est mieux adaptée que Collectors.groupingBy() . Cela vous permet de regrouper la liste dans une carte en fonction d'une condition de votre choix.

Votre état est une combinaison de id et firstName. Extrayons cette partie dans une propre méthode dans Person:

String uniqueAttributes() {
  return id + firstName;
}

La méthode getDuplicates() est maintenant assez simple:

public static List<Person> getDuplicates(final List<Person> personList) {
  return getDuplicatesMap(personList).values().stream()
      .filter(duplicates -> duplicates.size() > 1)
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
}

private static Map<String, List<Person>> getDuplicatesMap(List<Person> personList) {
  return personList.stream().collect(groupingBy(Person::uniqueAttributes));
}
  • La première ligne appelle une autre méthode getDuplicatesMap() pour créer la carte, comme expliqué ci-dessus.
  • Il diffuse ensuite les valeurs de la carte, qui sont des listes de personnes.
  • Il filtre tout sauf les listes de taille supérieure à 1, c’est-à-dire qu’il trouve les doublons.
  • Enfin, flatMap() est utilisé pour aplatir le flux de listes en un seul flux de personnes et rassemble le flux en une liste.

Une alternative, si vous identifiez réellement les personnes comme égales si vous avez les mêmes id et firstName est d'aller avec la solution de Jonathan Johx et de mettre en œuvre une méthode equals().

4
Magnilex

Dans ce scénario, vous devez écrire votre logique personnalisée pour extraire les doublons de la liste. Tous les doublons de la liste Person

   public static List<Person> extractDuplicates(final List<Person> personList) {

    return personList.stream().flatMap(i -> {
        final AtomicInteger count = new AtomicInteger();
        final List<Person> duplicatedPersons = new ArrayList<>();

        personList.forEach(p -> {

            if (p.getId().equals(i.getId()) && p.getFirstName().equals(i.getFirstName())) {
                count.getAndIncrement();
            }

            if (count.get() == 2) {
                duplicatedPersons.add(i);
            }

        });

        return duplicatedPersons.stream();
    }).collect(Collectors.toList());
}

Appliqué à:

 List<Person> l = new ArrayList<>();
           Person alex = new 
 Person.Builder().id(1L).firstName("alex").secondName("salgado").build();
            Person lolita = new 
 Person.Builder().id(2L).firstName("lolita").secondName("llanero").build();
            Person elpidio = new 
 Person.Builder().id(3L).firstName("elpidio").secondName("ramirez").build();
            Person romualdo = new 
 Person.Builder().id(4L).firstName("romualdo").secondName("gomez").build();
            Person otroRomualdo = new 
 Person.Builder().id(4L).firstName("romualdo").secondName("perez").build();
      l.add(alex);
      l.add(lolita);
      l.add(elpidio);
      l.add(romualdo);
      l.add(otroRomualdo);

Sortie:

[Person [id=4, firstName=romualdo, secondName=gomez], Person [id=4, firstName=romualdo, secondName=perez]]
3
Deadpool
List<Person> duplicates = personList.stream()
  .collect(Collectors.groupingBy(Person::getId))
  .entrySet().stream()
  .filter(e->e.getValue().size() > 1)
  .flatMap(e->e.getValue().stream())
  .collect(Collectors.toList());

Cela devrait vous donner une liste de Person où la id a été dupliquée.

2
YoYo

Je pense d’abord que vous devriez écraser la méthode equals de la classe Person et vous concentrer sur l’id et le nom. Et après vous pouvez le mettre à jour en ajoutant un filtre pour cela. 

@Override
public int hashCode() {
    return Objects.hash(id, name);
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final Person other = (Person) obj;
    if (!Objects.equals(name, other.name)) {
        return false;
    }
    if (!Objects.equals(id, other.id)) {
        return false;
    }
    return true;
}

 personList
       .stream() 
       .filter(p -> personList.contains(p))
       .collect(Collectors.toList());
1
Jonathan Johx
List<Person> arr = new ArrayList<>();
arr.add(alex);
arr.add(lolita);
arr.add(elpidio);
arr.add(romualdo);
arr.add(otroRomualdo);

Set<String> set = new HashSet<>();
List<Person> result = arr.stream()
                         .filter(data -> (set.add(data.name +";"+ Long.toString(data.id)) == false))
                         .collect(Collectors.toList());
arr.removeAll(result);
Set<String> set2 = new HashSet<>();
result.stream().forEach(data -> set2.add(data.name +";"+ Long.toString(data.id)));
List<Person> resultTwo = arr.stream()
                            .filter(data -> (set2.add(data.name +";"+ Long.toString(data.id)) == false))
                            .collect(Collectors.toList());
result.addAll(resultTwo);

Le code ci-dessus va filtrer en fonction du nom et de l'id. La liste de résultats contiendra tous les objets Personne dupliqués.

0
Lokesh Balaji