Este código remove duplicatas da lista original, mas eu quero extrair as duplicatas da lista original -> não removê-los (este nome do pacote é apenas parte de outro projeto):
Dado:
uma pessoa 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();
}
}
código de extração duplicação.
Observe aqui que filtrar o id eo primeiro nome para recuperar uma nova lista, eu vi isso em algum lugar código de outra pessoa, não a minha:
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));
}
}
O código de teste. Se você executar este caso de teste você receberá [alex, lolita, elpidio, romualdo].
Eu esperaria para chegar em vez [romualdo, otroRomualdo] como as duplicatas extraídos dado o id eo 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);
}
}
No meu trabalho eu era capaz de obter o resultado desejado lo usando Comparador usando TreeMap e ArrayList, mas esta foi a criação de uma lista, em seguida, filtrá-la, passando o filtro novamente para uma lista recém-criado, este código inchado aparência, (e provavelmente ineficiente)
Será que alguém tem uma idéia melhor como extrair duplicatas ?, não removê-los.
Desde já, obrigado.
atualização :
Obrigado a todos por suas respostas
Para remover o duplicado utilizando mesma abordagem com os 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()) ;
}
Para duplicatas identificar, nenhum método que eu conheço é mais adequado do que Collectors.groupingBy()
. Isso permite que você agrupe a lista em um mapa com base em uma condição de sua escolha.
Sua condição é uma combinação de id
e firstName
. Vamos extrair essa parte em um método próprio em Person
:
String uniqueAttributes() {
return id + firstName;
}
O getDuplicates()
método agora é bastante simples:
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));
}
- A primeira linha chama outro método
getDuplicatesMap()
para criar o mapa, como explicado acima. - Em seguida, córregos sobre os valores do mapa, que são listas de pessoas.
- Ele filtra tudo, exceto listas com um tamanho maior do que 1, ou seja, ele encontra as duplicatas.
- Finalmente,
flatMap()
é usado para nivelar o fluxo de listas em um único fluxo de pessoas, e coleta o fluxo para uma lista.
Uma alternativa, se você identificar verdadeiramente as pessoas como iguais se a ter o mesmo id
e firstName
é ir com a solução por Jonathan Johx e implementar um equals()
método.