Using Java 8 new constructs, such as streams for example, is there a way to filter a Set
based on the order in another collection?
Set<Person> persons = new HashSet<>();
persons.add(new Person("A", 23));
persons.add(new Person("B", 27));
persons.add(new Person("C", 20));
List<String> names = new ArrayList<>();
names.add("B");
names.add("A");
I want to filter items from set persons
based on names
, such that only those persons who have their names specified in names
will be retained, but in the order that they appear in names
.
So, I want
Set<Person> filteredPersons = ...;
where 1st element is Person("B", 27)
and 2nd element is Person("A", 23)
.
If I do the following,
Set<Person> filteredPersons = new HashSet<>(persons);
filteredPersons = filteredPersons.stream().filter(p -> names.contains(p.getName())).collect(Collectors.toSet());
the order is not guaranteed to be the same as in names
, if I am not mistaken.
I know how to achieve this using a simple for loop; I'm just looking for a java 8 way of doing it.
Thanks for looking!
EDIT:
The for loop that achieves the same result:
Set<Person> filteredPersons = new LinkedHashSet<>();
for (String name : names) {
for (Person person : persons) {
if (person.getName().equalsIgnoreCase(name)) {
filteredPersons.add(person);
break;
}
}
}
The LinkedHashSet
implementation ensures that the order is maintained.
After populating both the lists, we simply pass a Stream of Employee objects to the Stream of Department objects. Next, to filter records based on our two conditions, we're using the anyMatch predicate, inside which we have combined all the given conditions. Finally, we collect the result into filteredList.
More filters can be applied in a variety of methods, such using the filter() method twice or supplying another predicate to the Predicate.
The findAny() method returns any element from a Stream, while the findFirst() method returns the first element in a Stream.
Java stream provides a method filter() to filter stream elements on the basis of given predicate. Suppose you want to get only even elements of your list then you can do this easily with the help of filter method. This method takes predicate as an argument and returns a stream of consisting of resulted elements.
final Set<Person> persons = ...
Set<Person> filteredPersons = names.stream()
.flatMap(n ->
persons.stream().filter(p -> n.equals(p.getName()))
)
.collect(Collectors.toCollection(LinkedHashSet::new));
Collect the streams of persons created by filtering them by each name. This is fast for situations like the example provided, but will scale linearly with the number of persons, like O(N*P).
For larger collections of persons and names, creating an index that can be used to look up a person by name will be faster overall, scaling as O(N+P):
Map<String, Person> index = persons.stream()
.collect(Collectors.toMap(Person::getName, Function.identity()));
Set<Person> filteredPersons = names.stream()
.map(index::get)
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With