Collecting lists of optionals to a list containing present optionals - java

I am trying to refactor some code to return a list of optionals instead of an optional.
I have the following list in my class
private final List<Mapper<? extends Message>> mappers;
There is a private method that creates features for these mappers and returns a list of message
private List<Message> mapToFeature() {
mappers.stream()
.map(mapper -> mapper.createFeature())
.collect(Optionals.toList());
}
The Mapper interface looks like this:
public interface Mapper<T extends Message> {
Optional<T> createFeature();
}
The Optionals.toList() method returns a Collector to filter present optionals into a List.
I want to change the interface (and all the corresponding classes) to return a list of optionals
public interface Mapper<T extends Message> {
List<Optional<T>> createFeature();
}
I do not have a method in the Optionals util to filter present options from multiple lists. How would I be able to do the same without making any changes to the util class?

Collect a stream of Optionals to a list containing only the present values:
List<Optional<String>> someStrings = ...;
List<String> allPresentStrings = someStrings.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());

Since unable to understand your issue properly, it would be better if you will post your util class as well mean while see my go for this:
private List<Message> mapToFeature() {
return mappers.stream()
.map(mapper -> mapper.createFeature())
.flatMap(List::stream)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Optionals.toList());
}

Stream<Optional<T>> present = listOfOptionals.stream().filter(Optional::isPresent);
Stream<T> asTs = present.map(Optional::get);
// this is safe, because all remaining are present,
//convert Streams to Lists as usual with collect.
List<T> listTs = asTs.collect(Collectors.toList());

Related

How to write a Mockito test for Java 8 Predicate and distinctByKeys in Java

I have method for getting unique elements by using multiple keys like Employee id, name and age. Wrote a test case but filter section is not covering, so how to cover it?
List<Employee> employees = getEmployees().stream()
.filter(distinctByKey(Employee::getEmployeeId, Employee::getEmployeeName, Employee::getEmployeeAge))
.collect(Collectors.toList());
private <T> Predicate<T> distinctByKeys(Function<? super T, ?>... keyExtractors) {
final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
return t -> {
final List<?> keys = Arrays.stream(keyExtractors)
.map(ke -> ke.apply(t))
.collect(Collectors.toList());
return seen.putIfAbsent(keys, Boolean.TRUE) == null;
};
}
If really need this part of code, you can use a lib Guava with the annotation #VisibleForTesting and you can call this method in the test.
https://guava.dev/releases/19.0/api/docs/com/google/common/annotations/VisibleForTesting.html

Joining 2 streams from same object in java

I have a list of objects of class A defined as below:
class A {
private Set<String> sOne;
private Set<String> sTwo;
// Constructor, getters and setters
}
Now I would like to create a stream which contains elements of both sOne and stwo. Is there a way to do it in Java 8?
You can combine them using:
List<A> aList = ...;
Stream<String> stream = aList.stream()
.flatMap(a -> Stream.concat(
a.getsOne().stream(),
a.getsTwo().stream())
);
Stream.concat(sOne.stream(), sTwo.stream())
You should just be aware that this drops some characteristics IIRC in some cases.
An alternative to already mentioned Stream::concat is the Stream::of:
Stream.of(sOne.stream(), sTwo.stream())
.flatMap(Function.identity())
...
This requires to flatten the structure unless you wish to work with Stream<Stream<T>> or a stream of any collection Stream<Collection<T>>.

Do I need a custom Spliterator to avoid extra .stream() call?

I have this code which works fine, but I find it ugly.
#EqualsAndHashCode
public abstract class Actions {
#Getter
private List<ActionsBloc> blocs;
public Actions mergeWith(#NotNull Actions other) {
this.blocs = Stream.of(this.blocs, other.blocs)
.flatMap(Collection::stream)
.collect(groupingBy(ActionsBloc::getClass, reducing(ActionsBloc::mergeWith)))
.values()
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
return this;
}
}
ActionsBloc is a super type which contains a list of Action.
public interface ActionsBloc {
<T extends Action> List<T> actions();
default ActionsBloc mergeWith(ActionsBloc ab) {
this.actions().addAll(ab.actions());
return this;
}
}
What I want to do is merge blocs of Actions together based on the Class type. So I'm grouping by ActionsBloc::getClass and then merge by calling ActionsBloc::mergeWith.
What I find ugly is calling the values().stream() after the first stream was ended on collect.
Is there a way to operate only on one stream and get rid of values().stream(), or do I have to write a custom Spliterator? In other words have only one collect in my code.
You can work with a reducing identity to sort that out possibly. One way could be to update the implementation of mergeWith as :
default ActionsBloc mergeWith(ActionsBloc ab) {
this.actions().addAll(Optional.ofNullable(ab)
.map(ActionsBloc::actions)
.orElse(Collections.emptyList()));
return this;
}
and then modify the grouping and reduction to:
this.blocs = new ArrayList<>(Stream.of(this.blocs, other.blocs)
.flatMap(Collection::stream)
.collect(groupingBy(ActionsBloc::getClass, reducing(null, ActionsBloc::mergeWith)))
.values());
Edit: As Holger pointed out such use cases of using groupingBy and reducing further could be more appropriately implemented using toMap as :
this.blocs = new ArrayList<>(Stream.concat(this.blocs.stream(), other.blocs.stream())
.collect(Collectors.toMap(ActionsBloc::getClass, Function.identity(), ActionsBloc::mergeWith))
.values());

How to stream value of Java List (Varargs) in a method?

I have the following method:
public static List<A> getValuesExclusion(A exclusion) {
return Arrays.stream(values())
.filter(item -> item != exclusion)
.collect(Collectors.toList());
}
//this function returns enum list of A types that has no A type'exclusion'
Now I want to make it into a list as argument:
public static List<A> getValuesExclusion(A... exclusions){
return Arrays.stream(values())
.filter(???)
.collect(Collectors.toList());
}
My question is, how can I do the filter for the second case? I would like to retrieve an enum list that excludes all the values "exclusions" as input. Here are the attributes of class A:
public enum A implements multilingualA{
A("a"),
B("b"),
C("c"),
D("d");
...
}
If you want to make sure all the items are not included in the exclusions you could do:
public static List<A> getValuesExclusion(AType... exclusions){
return Arrays.stream(values())
.filter(e -> Arrays.stream(exclusions).noneMatch(c -> c == e))
.collect(Collectors.toList());
}
Which will create a Stream of exclusions and then use noneMatch() to ensure the given AType is not included in the Array
You should rethink whether List really is the appropriate data type for something containing unique elements. A Set usually is more appropriate.
Then, if you care for performance, you may implement it as
public static Set<A> getValuesExclusion(A... exclusions){
return exclusions.length == 0? EnumSet.allOf(A.class):
EnumSet.complementOf(EnumSet.of(exclusions[0], exclusions));
}
The class EnumSet is specifically designed for holding elements of an enum type, just storing a bit for each constant, to tell whether it is present or absent. This allows operations like complementOf, which just flips all bits using a single ⟨binary not⟩ operation, without the need to actually traverse the enum constants.
If you insist on returning a List, you can do it as easy as
public static List<A> getValuesExclusion(A... exclusions){
return new ArrayList<>(exclusions.length == 0? EnumSet.allOf(A.class):
EnumSet.complementOf(EnumSet.of(exclusions[0], exclusions)));
}
I would not go with Streams here but with the a (imho) more readable approach:
public static List<A> getValuesExclusion(AType... exclusions){
List<A> values = Arrays.asList(values());
values.removeAll(Arrays.asList(ex));
return values;
}

How to convert lambda filters with dynamic values to method references

I have some Java code which filters a list based on some input. It currently uses a lambda, for example:
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects
.stream()
.filter( compObject -> allowedTags.contains(compObject.getTag()))
.collect(Collectors.toList());
}
What I want to do is to move the filter logic to another method to make it re-usable and easily unit testable. So I wanted to use a method reference in place of the lambda passed to the filter method. Easy to do if the filter logic is fairly static (i.e. list of allowed tags is known at compile time) but I can't figure out how to do this with dynamic data in the filter.
What I wanted was some way to use a method reference and then pass the second dynamic param i.e.
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects
.stream()
.filter(this::filterByAllowedTags, allowedTags)
.collect(Collectors.toList());
}
So is it possible to do what I want or am I possibly approaching this situation incorrectly?
I'd suggest passing in a Predicate as a parameter. That way the caller can filter based on any criteria it wants, including allowedTags or whatever:
public List<ComplexObject> retrieveObjectsFilteredBy(Predicate<ComplexObject> pred) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects.stream()
.filter(pred)
.collect(Collectors.toList());
}
This would be called like so:
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveObjectsFilteredBy(cobj -> allowedTags.contains(cobj.getTag()));
But you could go even further, depending on how much refactoring you're willing to do. Instead of "retrieve" returning a List, how about having it return a Stream? And instead of the retrieve-filter method returning a List, how about having it return a Stream too?
public Stream<ComplexObject> retrieveObjectsFilteredBy2(Predicate<ComplexObject> pred) {
Stream<ComplexObject> complexObjects = retrieveAllComplexObjects2();
return complexObjects.filter(pred);
}
And the calling side would look like this:
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveObjectsFilteredBy2(cobj -> allowedTags.contains(cobj.getTag()))
.collect(toList());
Now if you look at it carefully, you can see that the retrieve-filter method isn't adding any value at all, so you might just as well inline it into the caller:
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveAllComplexObjects2()
.filter(cobj -> allowedTags.contains(cobj.getTag()))
.collect(toList());
Of course, depending upon what the caller wants to do, it might not want to collect the results into a list; it might want to process the results with forEach(), or something else.
Now you can still factor out the filter into its own method, for testing/debugging, and you can use a method reference:
boolean cobjFilter(ComplexObject cobj) {
List<String> allowedTags = ... ;
return allowedTags.contains(cobj.getTag());
}
List<ComplexObject> result =
retrieveAllComplexObjects2()
.filter(this::cobjFilter)
.collect(toList());
If you don't want the filter to have the allowed tags built into it, you can change it from being a predicate into a higher-order function that returns a predicate instead:
Predicate<ComplexObject> cobjFilter(List<String> allowedTags) {
return cobj -> allowedTags.contains(cobj.getTag());
}
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveAllComplexObjects2()
.filter(cobjFilter(allowedTags))
.collect(toList());
Which of these variations makes the most sense depends on what your application looks like and what kind of dynamicism you require in filtering.
How about the following? It extracts the predicate in a separate method to make it easily testable, and can be reused easily.
public Predicate<ComplexObject> tagAllowed(List<String> allowedTags) {
return (ComplexObject co) -> allowedTags.contains(co.getTag());
}
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects
.stream()
.filter(tagAllowed(allowedTags))
.collect(Collectors.toList());
}
The problem with the method reference this::filterByAllowedTags is that it has the shape:
(ComplexObject, List<String>) -> boolean
But it's being passed into filter() which expects a lambda of the shape:
(ComplexObject) -> boolean
In other words, this::filterByAllowedTags can never be a Predicate, but it could be some alternate interface Predicate2. Then you'd also need an overload of filter which takes a Predicate2.
Eclipse Collections (formerly GS Collections) has the method select() which behaves just like filter(), and selectWith() which behaves like the overload I just described.
Using select():
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
// retrieveAllComplexObjects returns a MutableList, possibly FastList
MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
// select() returns MutableList here which extends List
return complexObjects.select(compObject -> allowedTags.contains(compObject.getTag()));
}
Using selectWith():
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
// retrieveAllComplexObjects returns a MutableList, possibly FastList
MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
// select() returns MutableList here which extends List
return complexObjects.selectWith(this::filterByAllowedTags, allowedTags);
}
private boolean filterByAllowedTags(ComplexObject complexObject, List<String> allowedTags)
{
return allowedTags.contains(complexObject.getTag());
}
Note: I am a committer for Eclipse Collections.

Categories