I have a list of Person
objects and I want to remove similar (i.e. having the same `objects from it.
public class Person {
private String name;
private int age;
private boolean isSelfEmployed;
// getters, constructor, etc.
}
Sample data:
List<Person> personList = new ArrayList<>();
personList.add(new Person("John", 26, true)); // Include
personList.add(new Person("Erica", 29, false)); // Include
personList.add(new Person("John", 26, false)); // Exclude
For Person
objects having the same name
and age
I want to remove only those objects where isSelfEmployed
property is false
(i.e. the final list should contain only the first and the second objects).
I'm wonder is it possible to achieve using streams?
CodePudding user response:
We can group Person
objects by name
and age
using collector groupingBy()
that would give a Map with each value represented by a list of namesake people have the same and age.
Then we need to iterate over the values of the map, applying the logic for eliminating people having property isSelfEmployed
set to false
if there's more than one person mapped to the same key.
I assume that we can't rely on the implementation of the equals/hashCode
(it might be implemented differently than based on two fields we're interested in) and in order to group Person
objects I would use a Java 16 record as a key (we can also make use of a plain class for that, the option with record is less verbose).
And to implement filtering logic I would use mapMulty()
operation, which is meant for one-to-many transformations similarly to flatMap
and replaces a stream element with zero or more elements of a different type. This operation is also one of the features of Java 16.
List<Person> personList = List.of(
new Person("John", 26, true),
new Person("Erica", 29, false),
new Person("John", 26, false)
);
List<Person> result = personList.stream()
.collect(Collectors.groupingBy(
person -> {
record Key(String name, int age) {}
return new Key(person.getName(), person.getAge());
}))
.values().stream()
.<Person>mapMulti((list, consumer) -> {
if (list.size() == 1) list.forEach(consumer);
else list.stream().filter(Person::isSelfEmployed).forEach(consumer);
})
.toList();
result.forEach(System.out::println);
Output:
Person{name='Erica', age=29, isSelfEmployed=false}
Person{name='John', age=26, isSelfEmployed=true}
CodePudding user response:
I guess this is one of those questions which shouldn't be solved using the Stream API.
While Alexander Ivanchenko's answer provides a way to achieve this by using Stream, here is an example with normal loops:
Map<String, Person> map = new HashMap<>();
for (Person p: personList) {
// name#age as key
// assuming `#` will not appear at the end of name or at the beginning of age
// Using `#` because what is name is John2 and age is 25?
String k = p.name "#" p.age;
if (map.containsKey(k) && !p.isSelfEmployed) {
continue;
}
map.put(k, p);
}
A simple, recommended, another approach would be to override equals
and hashcode
of Person
class and simply add the List
to a Set
and the overridden methods will determine the uniqueness in the Set
.