Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Filter set based on another set

Tags:

java

java-8

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.

like image 243
Kesh Avatar asked Jan 11 '16 18:01

Kesh


People also ask

How do I filter a list based on another list in Java 8?

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.

Can we use multiple filters in Java 8?

More filters can be applied in a variety of methods, such using the filter() method twice or supplying another predicate to the Predicate.

What is the difference between findFirst and findAny Java 8?

The findAny() method returns any element from a Stream, while the findFirst() method returns the first element in a Stream.

How do you filter a list of objects using 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.


1 Answers

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));
like image 81
erickson Avatar answered Nov 03 '22 03:11

erickson