I have the following expression:
scheduleIntervalContainers.stream()
.filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime())
.collect(Collectors.toList());
...where scheduleIntervalContainers has element type ScheduleContainer:
final List<ScheduleContainer> scheduleIntervalContainers
Is it possible to check the type before the filter?
You can apply another filter in order to keep only the ScheduleIntervalContainer instances, and adding a map will save you the later casts :
scheduleIntervalContainers.stream()
.filter(sc -> sc instanceof ScheduleIntervalContainer)
.map (sc -> (ScheduleIntervalContainer) sc)
.filter(sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList());
Or, as Holger commented, you can replace the lambda expressions with method references if you prefer that style:
scheduleIntervalContainers.stream()
.filter(ScheduleIntervalContainer.class::isInstance)
.map (ScheduleIntervalContainer.class::cast)
.filter(sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList());
A pretty elegant option is to use method reference of class:
scheduleIntervalContainers
.stream()
.filter( ScheduleIntervalContainer.class::isInstance )
.map( ScheduleIntervalContainer.class::cast )
.filter( sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList() );
There is a small problem with #Eran solution - typing class name in both filter and map is error-prone - it is easy to forget to change the name of the class in both places. An improved solution would be something like this:
private static <T, R> Function<T, Stream<R>> select(Class<R> clazz) {
return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null;
}
scheduleIntervalContainers
.stream()
.flatMap(select(ScheduleIntervalContainer.class))
.filter( sic -> sic.getStartTime() != sic.getEndTime())
.collect(Collectors.toList());
However there might be a performance penalty in creating a Stream for every matching element. Be careful to use it on huge data sets. I've learned this solution from #Tagir Vailev
Instead of a filter + map like other answers suggest, I would recommend this utility method:
public static <Super, Sub extends Super> Function<Super, Stream<Sub>> filterType(Class<Sub> clz) {
return obj -> clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty();
}
Use it as:
Stream.of(dog, cat fish)
.flatMap(filterType(Dog.class));
Compared to filter + map it has the following advantages:
If the class does not extend your class you will get a compile error
Single place, you can never forget to change a class in either filter or map
Filter by class type with StreamEx
StreamEx.of(myCollection).select(TheThing.class).toList();
Related
I've this code:
private Iterable<Practitioner> pickPractitioners(List<String> ids) {
return Optional.ofNullable(ids)
.map(List::stream)
.orElse(Stream.of())
.collect(
Collectors.collectingAndThen(
Collectors.toList(),
this.practitionerRepository::findAllById
)
);
}
Problem is that when ids is empty, this.practitionerRepository::findAllById is also executed.
I'd like to avoid this step if resulting collector is empty.
Any ideas?
In general to skip that part of the finisher you could pass a lambda instead of a method reference and check if the input is empty:
.collect(
Collectors.collectingAndThen(
Collectors.toList(),
r -> r.isEmpty() ? Collections.emptyList() : this.practitionerRepository.findAllById(r)
)
);
If your actual code is a simple as this example then you don't need to use streams or optional at all. Instead you could just check if the input of the method is null or empty in a ternary operator:
return ids == null || ids.isEmpty() ? Collections.emptyList() :
this.practitionerRepository.findAllById(ids);
Whilst the practical part of this question (how to avoid interrogating the repository with an empty list as an argument) is already addressed in other answers I want to point out that there's a cleaner way to build a pipeline in this method.
Firstly it's worthy to remind that the main purpose of Optional.ofNullable() is to create an Optional object that has to be returned from a method.
Attempts to use Optional.ofNullable() in order to utilize method-chaining or to avoid null-checks in the middle of the method according to Stuart Marks are considered to be anti-patterns.
Here is the quote from his talk at Devoxx:
"it's generally a bad idea to create an Optional for the specific
purpose of chaining methods from it to get a value."
A similar idea was expressed in his answer on stackoverflow.
What are the alternatives?
Since Java 9 Stream interface has its own method ofNullable().
Returns a sequential Stream containing a single element, if non-null,
otherwise returns an empty Stream.
Keeping all that in mind method pickPractitioners() could be rewritten like this:
private Function<List<String>, Iterable<Practitioner>> getPractitioners =
idList -> idList.isEmpty() ? Collections.emptyList() :
this.practitionerRepository.findAllById(idList);
private Iterable<Practitioner> pickPractitioners(List<String> ids) {
return Stream.ofNullable(ids)
.flatMap(List::stream)
.collect(Collectors.collectingAndThen(
Collectors.toList(),
getPractitioners
));
}
If you look at the signature of the Finisher. It is just a function, so you can just write it:
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) {
static interface MyRepository extends JpaRepository<Part, Long> {
}
public static void main(String[] args) {
MyRepository myRepository = null;
List<Long> list = null;
Function<List<Long>, List<Part>> finisher = (ids) -> {
return ids.isEmpty() ? Collections.emptyList() : myRepository.findAllById(ids);
};
Optional.ofNullable(list)
.map(List::stream)
.orElse(Stream.of())
.collect(
Collectors.collectingAndThen(
Collectors.toList(),
finisher
)
);
}
I have code like this:
public void processList(List<String> list) {
for (String item : list) {
Object obj = getObjectForString(item);
if (obj != null) {
doSomethingWithObject(obj);
} else {
System.err.println("Object was null for " + item);
}
}
}
Ideally I would like to streamline this and avoid the null check using list.stream().map( *blah, blah, blah* ), and doSomethingWithObject if the object is not null, but log the error otherwise (by using the orElse method on an optional). I'm not super savvy with this Java 8 functionality and not sure if there is a nice, slick way to do what I want here or not. Suggestions?
Edit to add a failed attempt at this:
list.stream()
.map(p -> getObjectForString(p))
.map(Optional::ofNullable)
.forEach(
p -> p.ifPresentOrElse(
r -> doSomethingWithObject(r),
() -> System.err.println("Object was null")
));
Even if that code behaved the way I want, it still doesn't append the String from the original list to the error message as I would like it to. But maybe that's too much complexity to try to accomplish with streams like this.
we should propagate the item even after conversion. The slick way is using tuple or pair.
I used Tuple from vavr functional library to do the same. And below is the code for your reference
list.stream()
.map(p -> Tuple.of(p, getObjectForString(p)).map2(Optional::ofNullable))
.forEach(p -> p._2.ifPresentOrElse(
r -> doSomethingWithObject(r),
() -> System.err.println("Object was null" + p._1))
);
Another approach would be to collect the items in to separate 2 buckets/partitions based on if the item had an associated object or not. After that, process the 2 buckets as required:
final Boolean HAS_OBJECT = Boolean.FALSE;
Map<Boolean, List<String>> partitionedMap = list.stream()
.collect(Collectors.partitioningBy(item -> !Objects.isNull(getObjectForString(item))));
partitionedMap.get(HAS_OBJECT).stream()
.map(item -> getObjectForString(item))
.forEach(obj -> doSomethingWithObject(obj));
partitionedMap.get(!HAS_OBJECT)
.forEach(item -> System.err.println("Object was null for " + item));
Even though the below method does not avoid a null check as you wanted in your question, this is just another way to achieve the same result. (Only benefit is that it saves 1-2 lines of code!).
The below code uses Runnable (takes no arguments and returns nothing as well) along with Java 8's Function.
NOTE : I would still recommend the normal for loop :-), as I believe that the below might look fancy, but the for loop is more easy to understand in this particular case.
Function<String, Runnable> func = item -> {
Object obj = getObjectForString(item);
return (obj != null) ? ( () -> doSomethingWithObject(obj))
: ( () -> System.err.println("Object was null for " + item));
};
list.stream().map(func).forEach(Runnable::run);
I am new in Java8 and I want to refactor this piece of code and convert it in a more Java8 style,
for (RestaurantAddressee RestaurantAddressee : consultationRestaurant.getAddressees()) {
Chain chain = chainRestService.getClient().getChainDetails(getTDKUser(), RestaurantAddressee.getChain().getId());
if (chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId())) {
chainIds.add(restaurantAddressee.getChain().getId());
}
}
so I change it for this code:
consultationRestaurant.getAddressees()
.stream()
.map( ma -> chainRestService.getClient().getChainDetails(getTDKUser(), ma.getChain().getId()))
.filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
.forEach(chainIds.add(chain.getId()));
But I have this compilation error:
chain cannot be resolved
You forgot to specify the lambda expression parameter in your forEach call.
That said, you shouldn't use forEach to add elements to a collection. Use collect:
List<String> chainIds =
consultationRestaurant.getAddressees()
.stream()
.map( ma -> chainRestService.getClient().getChainDetails(getTDKUser(), ma.getChain().getId()))
.filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
.map(Chain::getId)
.collect(Collectors.toList());
Here. Your loop defines:
Chain chain = chainRestService.getClient()...
But your stream statement simply misses to define that variable.
So: in places that need that variable, you have to provide, for example as parameter:
filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
I have a requirement to validate a field against some predefined values (that can grow in future). So for this I have created a Enum and defined a method that returns the stream of the allowed values.
public enum EnumDemo {
VERSION("1.0.0","2.0.3");
private List<String> ver;
EnumDemo(String... ver) {
this.ver = Arrays.asList(ver);
}
public List<String> getVer() {
return ver;
}
public static Stream<EnumDemo> stream() {
return Arrays.stream(EnumDemo.values());
}
}
Now I need to validate a field against the values defined in this Enum.
I'm using:
Optional<EnumDemo> ab = EnumDemo.stream()
.map(l -> {l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();})
.findFirst();
System.out.println(ab.get().getVer());
But it is giving me compilation error. Any help would be appreciated.
Edit:
Compilation Error:
The method map(Function<? super EnumDemo,? extends R>) in the type Stream<EnumDemo> is not applicable for the arguments ((<no type> l) -> {})
You should write it this way:
Optional<EnumDemo> ab = EnumDemo.stream().filter(l -> l.getVer().contains("2.0.3"))
.findFirst();
By the way, it wasn't working because you used {} for the lambda expression, so it was expecting a return statement in the {}. You could either remove the {} (along with the ;) or add in the return.
Anyway the original codes looked confusing, not sure if I guessed the intention correctly, but this implementation should be clearer.
Edit
Based on your comment, this is what you need:
EnumDemo.stream().flatMap(l -> l.getVer().stream())
.filter("2.0.3"::equals)
.findAny()
.ifPresent(System.out::println);
Update
Holger commented that there is a shorter and more meaningful way, with better performance:
if(EnumDemo.stream()
.anyMatch(l -> l.getVer().contains(userString))) {
System.out.println(userString);
}
To understand it, you have to think about lambdas. Lambdas represent interfaces but are specially treated by the JVM, so not every Lambda needs a class to represent. (Stateless lambdas can be just methods).
Now when looking at the map() method in the Stream interface:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
You see that it expects an implementation of the Function interface. You now have many different ways to provide that mapper. In this example lets map from Object to String:
1. Using an inline lambda:
.map(o -> o.toString())
2. Using a multiline lambda:
.map(o -> {
return o.toString();
})
3. Using method references:
.map(Object::toString)
4. Using an anonymous class:
.map(new Function<Object, String>(){
#Override
public String apply(Object o){
return o.toString();
}
})
Your current code uses the 2. approach. But without a return statement. This is even better seen when looking at the anonymous class at 4.. It seems natural, that when not using a return statement in a method that no value is returned.
And that's why you get the compilation error.
You just have to add the return statement:
.map(l -> {
return l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();
});
Or remove the brackets {}:
.map(l -> l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst());
Or even use the approach provided by #Jai in his answer. Which works even better, than what you currently have.
You are using lambda expression and not returning any value so it is giving compilation error. It is better to use ifPresent()
String val="2.0.3";
EnumDemo.stream()
.flatMap(l -> l.getVer().stream())
.filter(c -> c.equals(val))
.findAny()
.ifPresent(x -> System.out.println(x));
I have googled quite a bit, but didn't found an answer.
Here is what I have:
parentList.forEach(p -> {
childList
.stream()
.filter(c -> p.id() == c.parentId())
.<...continue working on stream...>
});
I cannot find a way how to replace "filter" part with a Predicate as it seems that I need to pass argument to Predicate?
Your problem is that you're using a different Predicate each time, because although c is the parameter to your predicate, p also varies:
final Node p;
Predicate<Node> matchesParentId = c -> p.id() == c.id();
The reason your existing code compiles OK is that p is effectively final in the scope of the forEach block, so it can be used as a final field in a Predicate within that scope, with a lifetime of one forEach iteration.
You could do:
parentList.forEach(p -> {
childList
.stream()
.filter(matchesId(p))
.<...continue working on stream...>
});
private Predicate<Node> matchesId(Node other) {
return node -> node.id() == other.id();
}
But you won't be able to create one Predicate and reuse it as p varies.
You could write a BiPredicate and curry it into a Predicate. Unfortunately Java doesn't provide a curry method, so you have to provide your own.
private <T,U> Predicate<U> curry(BiPredicate<T,U> biPredicate, T t) {
return u -> biPredicate.test(t, u);
}
BiPredicate<Node,Node> nodesMatch = (a,b) -> a.id() == b.id();
parentList.forEach(p -> {
childList
.stream()
.filter(curry(nodesMatch, p))
.<...continue working on stream...>
});
This doesn't buy you all that much over and above the previous solution, but it's a bit more FP-nerdy. You're still creating a new Predicate for every p. Of course you could inline it rather than use the curry() method.
.filter(c -> nodesMatch.test(p, c))
It does mean you could have a selection of BiPredicate<Node,Node>s to plug in dynamically. If your BiPredicate were expensive to initialise, the many Predicates wrapped around it by currying would be cheap.
Or, you could map p and c into a single object, which allows you to submit the whole thing to a predicate:
Predicate<Pair<Node,Node>> nodesMatch = pair ->
pair.left().id() == pair.right().id();
parentList.forEach(p -> {
childList
.stream()
.map(c -> new Pair<Node>( c, p))
.filter(nodesMatch)
.map( pair -> pair.left() )
.<...continue working on stream...>
});
(Pair here is hypothetical, but a number of 3rd party libraries (e.g. Guava) provide one, or roll your own, or use new Node[] { c, p })