Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve distinct element based on multiple attributes of java object using java 8 stream

How can I get the distinct element from list based on multiple condition using java 8 stream ?

For example - Let's assume an object Person :

class Person {
    Integer id;
    String name;
}

I want to have a list with unique combination of id and name.
There can be multiple records with same id and name in list

like image 934
seasagar Avatar asked Jan 09 '18 09:01

seasagar


3 Answers

You can create your own distinct method for example :

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

So you can use it with filter :

List<Person> persons = listPersons.stream()
         //@Holger suggest
        .filter(distinctByKey(pr -> Arrays.asList(pr.getId(), pr.getName())))
        .collect(toList());

Take a look at this :

  • Thank you Stuart Marks for the useful distinctByKey method
  • https://www.concretepage.com/java/jdk-8/java-8-distinct-example
  • https://howtodoinjava.com/java-8/java-stream-distinct-examples/
  • demo ideone

If your list person is :

List<Person> listPersons = Arrays.asList(
        new Person(1, "person1"),
        new Person(1, "person5"),
        new Person(2, "person2"),
        new Person(1, "person1"),
        new Person(1, "person2"),
        new Person(1, "person1"),
        new Person(3, "person3")
);

Outputs

Person{id=1, name=person1}
Person{id=1, name=person5}
Person{id=2, name=person2}
Person{id=1, name=person2}
Person{id=3, name=person3}
like image 161
YCF_L Avatar answered Oct 17 '22 10:10

YCF_L


Override hashCode() and equals() methods for your class, then you can do something like this:

Set<Person> result = persons.stream().collect(Collectors.toSet());

Set is a data structure that does not allow duplicates so this way you'll only have unique elements.

Here is a solution: https://ideone.com/e.js/G7T2rH

like image 33
мајдејсаремех Avatar answered Oct 17 '22 08:10

мајдејсаремех


Below are the steps to get the solution.

  1. You need to implement equals and hashcode in class Person for equality check. If you have overridden these methods in your class, whenever there is an equality check, the methods implemented in Person will be called and will always return the unique result.

     class Person {
    
     Integer id;
     String name;
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         Person p = (Person) o;
         return id == p.id && Objects.equals(name, p.name);
     }
     @Override
     public int hashCode() {
     return Objects.hash(id, name);
    

    }

}

  1. Use any of the below methods for getting distinct Person value:

    a). Use Set because Set contains only unique values. It's an unordered collection.

     Set<Person> set=new HashSet<Person>();
    
      for(Person c:list){
         set.add(c);
         }
    

    b). Using Java8 Stream API:

     List<Person> unique = list.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingInt(Person::getId))),
                     ArrayList::new));
    

    c). If ordered collection is required:

     LinkedHashSet<Person> lset=new LinkedHashSet<Person>();
     for(Personc:list){
         lset.add(c);
     }
    

    d) Java 8 Stream API method:

     List<Person> ll= list.stream().distinct().collect(Collectors.toList());
     ll.forEach((k) -> System.out.println(k.getId()+" & "+k.getName()));
    
like image 25
garima garg Avatar answered Oct 17 '22 09:10

garima garg