wake-up-neo.com

Extrahieren Sie doppelte Objekte aus einer Liste in Java 8

Dieser Code entfernt Duplikate aus der ursprünglichen Liste, aber ich möchte die Duplikate aus der ursprünglichen Liste extrahieren -> sie nicht entfernen (dieser Paketname ist nur Teil eines anderen Projekts):

Gegeben:

eine Person 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();
}
}

Extraktionscode für die Duplikation.

Beachten Sie, dass hier die ID und der Vorname gefiltert werden, um eine neue Liste abzurufen. Diesen Code habe ich an einem anderen Ort gesehen, nicht bei mir:

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

}

}

Der Testcode. Wenn Sie diesen Testfall ausführen, erhalten Sie [alex, lolita, elpidio, romualdo].

Ich würde erwarten, stattdessen [romualdo, otroRomualdo] als extrahierte Duplikate mit der ID und dem Vornamen zu erhalten:

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

}

}

In meinem Job konnte ich das gewünschte Ergebnis mithilfe von Comparator mithilfe von TreeMap und ArrayList erzielen. Dies erstellte jedoch eine Liste, die dann gefiltert und der Filter erneut an eine neu erstellte Liste übergeben wurde. Dies sieht aufgeblähten Code aus (und wahrscheinlich ineffizient)

Hat jemand eine bessere Idee, wie Duplikate extrahieren soll, entfernen Sie diese nicht.

Danke im Voraus. 

Update :

Vielen Dank an alle für Ihre Antworten

So entfernen Sie das Duplikat auf dieselbe Weise mit den 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()) ;
}

Zum Identifizieren von Duplikaten ist keine Methode, die ich kenne, besser geeignet als Collectors.groupingBy() . Auf diese Weise können Sie die Liste basierend auf einer Bedingung Ihrer Wahl in einer Karte gruppieren.

Ihre Bedingung ist eine Kombination aus id und firstName. Lassen Sie uns diesen Teil in Person in eine eigene Methode extrahieren:

String uniqueAttributes() {
  return id + firstName;
}

Die getDuplicates()-Methode ist jetzt ganz einfach:

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));
}
  • Die erste Zeile ruft eine andere Methode getDuplicatesMap() auf, um die Karte wie oben beschrieben zu erstellen.
  • Es strömt dann über die Werte der Karte, die Personenlisten sind.
  • Es filtert alles außer Listen mit einer Größe größer als 1 heraus, d. H. Es findet die Duplikate.
  • Schließlich wird flatMap() verwendet, um den Stream von Listen in einen einzigen Personenstrom zu glätten, und sammelt den Stream in eine Liste.

Eine Alternative, wenn Sie wirklich Personen als gleichwertig identifizieren, wenn die gleiche Variable id und firstName zu der Lösung von Jonathan Johx gehört und eine equals()-Methode implementiert.

4
Magnilex

In diesem Szenario müssen Sie Ihre benutzerdefinierte Logik schreiben, um die Duplikate aus der Liste zu extrahieren. Sie erhalten alle Duplikate in der 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());
}

Angewendet:

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

Ausgabe:

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

Das sollte Ihnen eine Liste von Person geben, in der die id dupliziert wurde.

2
YoYo

Ich denke, zuerst sollten Sie gleich Methode der Person-Klasse überschreiben und sich auf ID und Name konzentrieren. Und nachdem Sie es aktualisieren können, fügen Sie dazu einen Filter hinzu. 

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

Der obige Code wird nach Name und ID gefiltert. Die Ergebnisliste enthält alle duplizierten Personenobjekte

0
Lokesh Balaji