SyncMaster
SyncMaster

Reputation: 9966

How to get a Set from a list of objects using Java Streams

This might be a simple Java streams question. Say, I have a List<Student> object.

public class Student {
    public String name;
    public Set<String> subjects;

    public Set<String> getSubjects() {
        return subjects;
    }
}

How can I get all the subjects taken by the list of students?

I can do this using a for each loop. How can I convert the below code to use Streams?

for (Student student : students) {
    subjectsTaken.addAll(student.getSubjects());
}

Here is my attempt at using Java 8 streams. This gives me an Incompatible types error.

Set<String> subjectsTaken = students.stream()
        .map(student -> student.getSubjects())
        .collect(Collectors.toSet());

Upvotes: 5

Views: 5642

Answers (3)

HPH
HPH

Reputation: 398

A different alternative using Stream<T>#<R>collect :

students.stream()
    .map(Student::getSubjects)
    .<Set<String>>collect(HashSet::new, Collection::addAll, Collection::addAll)

Upvotes: 1

ETO
ETO

Reputation: 7299

Try this:

Set<String> subjectsTaken = 
                   students.stream()
                           .map(Student::getSubjects)
                           .flatMap(Set::stream) 
                           .collect(Collectors.toSet());

The idea is to map the students to their subjects first, then flatten the Stream<Set<String>> to Stream<String> and finally collect the stream to a Set.


I would suggest you to use method references instead of lambda expressions where it's possible (if it doesn't degrade readability).

Upvotes: 5

Eran
Eran

Reputation: 394136

Your current code produces a Set<Set<String>>, not a Set<String>.

You should use flatMap, not map:

Set<String> subjectsTaken = 
    students.stream() // Stream<Student>
           .flatMap(student -> student.getSubjects().stream()) // Stream<String>
           .collect(Collectors.toSet()); // Set<String>

Upvotes: 6

Related Questions