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

11,341

Solution 1

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 :

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}

Solution 2

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

Solution 3

You can filter your list first to get the elements that match the criteria and then use distinct.

List<element> getDistinctForCrit(Filter<element> pred) {
 //assuming list is already available in this class
 return list.stream().filter(pred).distinct().collect(Collectors.toList())
}

You can pass your filter criteria to this function and then the distinct will get the unique values for you.

distinct() takes produces distinct value based on the object comparison.

Therefore you should give your uniqueness logic in your equals() method of the class

Solution 4

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()));
    
Share:
11,341
seasagar
Author by

seasagar

Updated on June 12, 2022

Comments

  • seasagar
    seasagar almost 2 years

    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