Collect all values of a Set field - java

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.

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.

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());

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());

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.

Related

Correct use of Java 8 supplier consumer

I'm still strugling with Suppliers and Consumers for Java 8, I have this:
final Set<String> roles = new HashSet<>();
user.getRoleGroups().forEach(rg -> rg.getRoles().forEach(r -> roles.add(r.getName())));
To get a Set from role names that are inside a list of Roles inside a list of RoleGroups.
Pretty sure I could use something in one line with .stream().map() and RoleGroup::getRoles and Role::getName to get this Set. But I don't know how.
You are pretty close! To use a Stream instead, do something like this:
final Set<String> roles = user.getRoleGroups().stream()
.flatMap(g -> g.getRoles().stream())
.map(Role::getName)
.collect(Collectors.toSet());
Use of flatMap() is the only tricky part here. The flatMap() operation transforms an element to a Stream, which is concatenated with the Streams from the other elements.

Joining all subsets within a stream in Java optionals

This is the original piece of code:
Set<StatuteType> statuteTypes = registration.getStudent().getStudentStatutesSet()
.stream()
.map(StudentStatute_Base::getType)
.collect(Collectors.toSet());
I want to wrap everything in an Optional to avoid null pointers and all. If the student does not exist or the statutesSet does not exist.
What I have:
Set<StatuteType> statuteTypes = Optional.of(registration)
.map(Registration_Base::getStudent)
.map(student -> student.getStudentStatutesSet())
.flatMap(Collection::stream)
.map(StudentStatute_Base::getType)
.collect(Collectors.toSet())
.orElse(null);
Would something like this be in someway possible? I want to avoid null checks in this chain, and if there's any null just return a simple null as well instead getting an exception.
Normally what I think would be logical would be to use a flatMap as described here but it doesn't seem to be correct in this case, because the Optional flatmap returns an Optional.
Here's a simple way of doing it:
Set<StatuteType> statuteTypes = Optional.ofNullable(registration)
.map(Registration_Base::getStudent)
.map(student -> student.getStudentStatutesSet())
.map(Collection::stream)
.orElseGet(Stream::empty) // Exit Optional, enter stream
.map(StudentStatute_Base::getType)
.collect(Collectors.toSet());
However, it does not result in a null set. Collections should never be null, only empty. I would recommend this approach. The whole point of using an Optional object is so you never have to deal with null values.
Collection::stream does not return an Optional, so you should not use flatMap here. You should keep using map on the optional.
.map(Collection::stream) gives you an Optional<Stream<Statute>>. You seem to be trying to call the stream's map and collect methods on this. But you need to first call Optional.map before you can do that.
You should also use Optional.ofNullable if registration could be null:
Set<StatuteType> statuteTypes = Optional.ofNullable(registration)
.map(Registration_Base::getStudent)
.map(student -> student.getStudentStatutesSet())
.map(Collection::stream)
.map(x -> // Optional.map
x.map(StudentStatute_Base::getType) // Stream.map
.filter(Objects::nonNull) // I assume you want to filter out the statute types which are null?
.collect(Collectors.toSet())
)
.orElse(null);

Get rid of duplicates in one line instead of two with java 8 stream

Could it be written in one line of code instead of two separate? Because I tried adding .distinct() in the first line and somehow it didn't work. I'm not getting the difference here.
List<BgwContract> contractListWithDuplicates = monthlyFeePaymentList
.stream()
.map(MonthlyFeePayment::getBgwContract)
.collect(Collectors.toList());
List<BgwContract> contractListWithoutDuplicates = contractListWithDuplicates
.stream()
.distinct()
.collect(Collectors.toList());
You can use distinct with your existing Stream itself :
List<BgwContract> contractListWithDuplicates = monthlyFeePaymentList
.stream()
.map(MonthlyFeePayment::getBgwContract) // Stream<BgwContract>
.distinct() // here
.collect(Collectors.toList());
Since you wanted an explanation regarding the correct place of distinct:
When you write:
List<BgwContract> contractListWithDuplicates = monthlyFeePaymentList
.stream()
.distinct()
.map(MonthlyFeePayment::getBgwContract)
.collect(Collectors.toList());
you get a Stream of distinct MonthlyFeePayment instances (based on the equals implementation of MonthlyFeePayment class) and then map them to BgwContract instances. Two distinct MonthlyFeePayment instance may be mapped to the same BgwContract instance, so the output List may have duplicates.
When you write:
List<BgwContract> contractListWithDuplicates = monthlyFeePaymentList
.stream()
.map(MonthlyFeePayment::getBgwContract)
.distinct()
.collect(Collectors.toList());
you first map the MonthlyFeePayment instances to BgwContract instances, and only then remove the duplicates with distinct(), which is what you want.
With dintinct() the elements are compared using the equals() method.
You have to override equals() for your BgwContract object.

List containing another list to set using lambda in java

I have a list containing Persons, each person has a list with his subjects inside.
I need to return a Set containing every subject using lambda, so far i've tried this:
list.stream().map(person -> person.getSubjects());
But that would get me a List> so i can't use it.
How could i print/get every string in the list of every person using lambdas?
Thanks.
list.stream().map(person -> person.getSubjects().stream()); is not a List, it's a Stream. If you want a Set, do this :
list.stream().flatMap(person -> person.getSubjects().stream()).collect(Collectors.toSet());
This will create an HashSet<Subject>. Note the use of flatMap instead of map to flatten the lists of subjects into a single stream. If you want another implementation of Set, for example TreeSet, do the following :
list.stream().flatMap(person -> person.getSubjects().stream())
.collect(Collectors.toCollection(TreeSet::new));
You can use flatMap:
Set<Subject> subjects = list.stream()
.map(person -> person.getSubjects())
.flatMap(subjects -> subjects.stream())
.collect(Collectors.toSet());
flatMap is good for "flattening" nested collections.

Nesting java8 streams to get a collection by Predicate

I am trying to get a collection of Objects, from a Collection using streams in Java8 based on some predicate
Here is what I have tried, but there is some syntax error in this.:
Collection<object> objectCollectionNew = objectCollection.stream().filter(o -> objectCollection.stream().filter( x- > x.isTrue == o.isTrue));
So basically I want to get a collection of objects out of the objectCollection based on my Predicate. Also I am unsure about how to collect this into my Collection<object>.
.collect(Collectors.to ?? )
Some help will be appreciated.
It's not entirely clear what you are trying to do, but if you want to find all elements of your collection whose isTrue is equal to isTrue of some other element of the collection, you would do it like this:
objectColelction.stream()
.filter(o -> objectColelction.stream().anyMatch(x -> x.isTrue == o.isTrue))
.collect(Collectors.toList());
It seems like you want to use a flatMap :
Collection<object> objectCollectionNew = objectCollection.stream()
.flatMap(o -> objectCollection.stream().filter(x -> x.isTrue == o.isTrue))
.collect(Collectors.toList());
I had a similar problem which looks like an error in the Eclipse syntax parser. I solved it by adding parenthesis
Collection<object> objectCollectionNew = objectCollection.stream()
.filter( x -> {return x.isTrue == o.isTrue;} )
.collect(Collectors.toList());
Just answering on the syntax aspect, not how to best solve your filtering itself.

Categories

Resources