Retrieve distinct element based on multiple attributes of java object using java 8 stream
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 :
- 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}
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.
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);
}
}
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()));
seasagar
Updated on June 12, 2022Comments
-
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
andname
.
There can be multiple records with same id and name in list