title: Extract duplicate objects from a List in Java 8
body: <p>This code removes duplicates from the original list, but I want to extract the duplicates from the original list -> not removing them (this package name is just part of another project):</p>
  
  <p>Given:</p>
  
  <p>a Person pojo:</p>
  
  <pre><code>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></pre>
  
  <p>Duplication extraction code.</p>
  
  <p>Notice here we filter the id and the first name to retrieve a new list, I saw this code someplace else, not mine:</p>
  
  <pre><code>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&lt;Person&gt; getDuplicates(final List&lt;Person&gt; personList) {
  
     return personList
             .stream()
             .filter(duplicateByKey(Person::getId))
             .filter(duplicateByKey(Person::getFirstName))
             .collect(Collectors.toList());
  
  }
  
  private static &lt;T&gt; Predicate&lt;T&gt; duplicateByKey(final Function&lt;? super T, Object&gt; keyExtractor) {
      Map&lt;Object,Boolean&gt; seen = new ConcurrentHashMap&lt;&gt;();
      return t -&gt; isNull(seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE));
  
  }
  
  }
  </code></pre>
  
  <p>The test code. 
  If you run this test case you will get [alex, lolita, elpidio, romualdo].</p>
  
  <p>I would expect to get instead [romualdo, otroRomualdo] as the extracted duplicates given the id and the firstName:</p>
  
  <pre><code>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&lt;Person&gt; personList = new ArrayList&lt;&gt;();
  
      personList.add(alex);
      personList.add(lolita);
      personList.add(elpidio);
      personList.add(romualdo);
      personList.add(otroRomualdo);
  
      final List&lt;Person&gt; duplicates = DuplicatePersonFilter.getDuplicates(personList);
  
      LOGGER.info("Duplicates: {}",duplicates);
  
  }
  
  }
  </code></pre>
  
  <p>In my job I was able to get the desired result it by using Comparator using TreeMap and ArrayList, but this was creating a list then filtering it, passing the filter again to a newly created list, this looks bloated code, (and probably inefficient)</p>
  
  <p>Does someone has a better idea how to extract duplicates?, not remove them.</p>
  
  <p>Thanks in advance. </p>
  
  <p><strong>Update</strong></p>
  
  <p>Thanks everyone for your answers</p>
  
  <p>To remove the duplicate using same approach with the uniqueAttributes:</p>
  
  <pre><code>  public static List&lt;Person&gt; removeDuplicates(List&lt;Person&gt; personList) {
      return getDuplicatesMap(personList).values().stream()
              .filter(duplicates -&gt; duplicates.size() &gt; 1)
              .flatMap(Collection::stream)
              .collect(Collectors.toList());
  }
  
  private static Map&lt;String, List&lt;Person&gt;&gt; getDuplicatesMap(List&lt;Person&gt; personList) {
      return personList.stream().collect(groupingBy(DuplicatePersonFilter::uniqueAttributes));
  }
  
  private static String uniqueAttributes(Person person){
  
      if(Objects.isNull(person)){
          return StringUtils.EMPTY;
      }
  
      return (person.getId()) + (person.getFirstName()) ;
  }
  </code></pre>
  
  <p><strong>Update 2</strong></p>
  
  <p>But also the answer provided by @brett-ryan is correct:</p>
  
  <pre><code>public static List&lt;Person&gt; extractDuplicatesWithIdentityCountingV2(final List&lt;Person&gt; personList){
  
          List&lt;Person&gt; duplicates = personList.stream()
                  .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                  .entrySet().stream()
                  .filter(n -&gt; n.getValue() &gt; 1)
                  .flatMap(n -&gt; nCopies(n.getValue().intValue(), n.getKey()).stream())
                  .collect(toList());
  
          return duplicates;
  
      }
  </code></pre>
  
  <p><strong>EDIT</strong></p>
  
  <p>Above code can be found under:</p>
  
  <p><a href="https://gitlab.com/totopoloco/marco_utilities/-/tree/master/duplicates_exercises" rel="nofollow noreferrer">https://gitlab.com/totopoloco/marco_utilities/-/tree/master/duplicates_exercises</a></p>
  
  <p>Please see:</p>
  
  <p>Usage:
  <a href="https://gitlab.com/totopoloco/marco_utilities/-/blob/master/duplicates_exercises/src/test/java/at/mavila/exercises/duplicates/lists/DuplicatePersonFilterTest.java" rel="nofollow noreferrer">https://gitlab.com/totopoloco/marco_utilities/-/blob/master/duplicates_exercises/src/test/java/at/mavila/exercises/duplicates/lists/DuplicatePersonFilterTest.java</a> </p>
  
  <p>Implementation:
  <a href="https://gitlab.com/totopoloco/marco_utilities/-/blob/master/duplicates_exercises/src/main/java/at/mavila/exercises/duplicates/lists/DuplicatePersonFilter.java" rel="nofollow noreferrer">https://gitlab.com/totopoloco/marco_utilities/-/blob/master/duplicates_exercises/src/main/java/at/mavila/exercises/duplicates/lists/DuplicatePersonFilter.java</a></p>