Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collect all values of a Set field

I have a collection which has a field of type Set with some values. I need to create a new set collecting all these values.

I am wondering if this is possible using lambda expressions.

Below is the code line :

Set<String> teacherId = batches.stream()
                               .filter(b -> !CollectionUtils.isEmpty(b.getTeacherIds()))
                               .map(b -> b.getTeacherIds())
                               .collect(Collectors.toSet());

The problem is post map operation, it contains a collection of set of strings. So collect operation returns a Set<Set<String>> but i am looking to aggregate all the values to a single set.

like image 741
Biscuit Coder Avatar asked Jul 20 '17 08:07

Biscuit Coder


4 Answers

You need to use flatMap instead of map:

Set<String> teacherIds = 
    batches.stream()
           .flatMap(b -> b.getTeacherIds().stream())
           .collect(Collectors.toSet());

Note that the filtering is redundant for empty collections - streaming an empty collection will just result in an empty stream, which won't affect the final result. If getTeacherIds() could return null, however, you'd still have to handle it. Using filter(Objects::nonNull) would suffice, and save you the dependency on Apache Commons.

like image 175
Mureinik Avatar answered Oct 20 '22 00:10

Mureinik


You can use flatMap to obtain a flat Stream of all the values, and then collect to a Set<String>:

Set<String> teacherId = 
    batches.stream()
           .filter(b -> !CollectionUtils.isEmpty(b.getTeacherIds()))
           .flatMap(b -> b.getTeacherIds().stream())
           .collect(Collectors.toSet());
like image 45
Eran Avatar answered Oct 20 '22 00:10

Eran


If you care that that getTeacherIds() is not null, use it explicitly via !=, that CollectionUtils.isEmpty just hides stuff. Especially since if getTeacherIds() returns an Empty collection - that is handled just fine by flatMap, so to me that is not needed at all.

  Set<String> teacherIds = batches
            .stream()
            .filter(x -> x.getTeacherIds() != null)
            .flatMap(x -> x.getTeacherIds().stream())
            .collect(Collectors.toSet());
like image 24
Eugene Avatar answered Oct 20 '22 00:10

Eugene


I am wondering if this is possible using lambda expressions.

I capture the last fish, :).

Set<String> teacherIds = batches.stream()//v--- the class of `x`
                                .map(XClass::getTeacherIds)
                                .filter(Objects::nonNull)
                                .flatMap(Collection::stream)
                                .collect(Collectors.toSet());

Note: I'm sorry I'm forget to tell you if the getTeacherIds copy the internal IDs to a new set of IDs, the code above is appropriate for you. since it is read the IDs from XClass once.

like image 30
holi-java Avatar answered Oct 20 '22 00:10

holi-java