In one of the method we used lambda expression (below) nesting streams.
return req.gUs().stream()
.map((**cfg) -> {
.filter(Optional::isPresent)
.map(Optional::get);
I want to move part of the code using some helper methods and method reference (below).
(cfg) -> {
return rsp.getUs().stream()
.filter(result.getId())
.map((result) -> gEvent(index, Builder, cfg));
Any suggestions are greatly appreciated.
You can make a method, that returns a Function:
return req.getUs().stream()
.map(myFunction(rsp, index, headerBuilder))
.flatMap(stream -> stream)
.filter(Optional::isPresent)
.map(Optional::get);
private Function<CFGType, GenerateReturnType> myFunction(RspType rsp, IndexType index, HeaderType header){
return (cfg) -> {
return rsp.getPerUs().stream()
.filter((result) -> cfg.getId() == result.getId())
.filter((result) -> result.getCode() == ResultCode.SUCCESS)
.map((result) -> generateEvent(index, headerBuilder, cfg));
}
}
Or you could use a method reference if the rsp, index and header are fields:
return req.getUs().stream()
.map(this::myFunction)
.flatMap(stream -> stream)
.filter(Optional::isPresent)
.map(Optional::get);
private GenerateType myFunction(CFGType cfg) {
return rsp.getUs().stream()
.filter((result) -> cfg.getUsChId() == result.getChId())
.filter((result) -> result.getResultCode() == ResultCode.SUCCESS)
.map((result) -> generateEvent(index, headerBuilder, cfg));
}
Related
How can I print out the result of the .collect() while it is processing?
When I try a forEach, I get a compiler error.
filter and peek only apply to streams.
var landmarks = Set.of("Eiffel Tower", "Statue of Liberty",
"Stonehenge", "Mount Fuji");
var result = landmarks
.stream()
.collect(Collectors.partitioningBy(b -> b.contains(" ")))
<---------------what to put here?
.entrySet()
.stream()
.flatMap(t -> t.getValue().stream())
.collect(Collectors.groupingBy(s -> !s.startsWith("S")));
If you are using IntelliJ, you can use the Trace Current Strean Chain functionality while debugging.
Unfortunately, it's impossible to achieve what you're asking in a concise way, since Java doesn't support extension or postfix functions. So, there is no way to add new method (e.g. .debugPrint()) into Map (the result of .collect).
( If you'd asked this question for Kotlin, the answer would've been to use .also{}. )
However, there are couple of tricks that come close.
1. The easy way
Just wrap the whole chain in the function that prints and returns the result.
Pros: easy to write
Cons: breaks the nice formatting of the chain
var result = printed(
landmarks
.stream()
.collect(Collectors.partitioningBy(b -> b.contains(" ")))
)
.entrySet()
.stream()
.flatMap(t -> t.getValue().stream())
.collect(Collectors.groupingBy(s -> !s.startsWith("S")));
// ...
// required helper function
static <T> T printed(T any) {
System.out.println(any);
return any;
}
2. The verbose way
You can create a function that wraps the Collector and prints it's result after it's finished collecting.
Pros: stream chain still looks nice
Cons: collector wrapper implementation is pretty verbose
var result = landmarks
.stream()
.collect(printed(Collectors.partitioningBy(b -> b.contains(" "))))
.entrySet()
.stream()
.flatMap(t -> t.getValue().stream())
.collect(Collectors.groupingBy(s -> !s.startsWith("S")));
// ...
// The required helper function
static <T, A, R> Collector<T, A, R> printed(Collector<T, A, R> wrapped) {
return new Collector<T, A, R>() {
#Override
public Supplier<A> supplier() {
return wrapped.supplier();
}
#Override
public BiConsumer<A, T> accumulator() {
return wrapped.accumulator();
}
#Override
public BinaryOperator<A> combiner() {
return wrapped.combiner();
}
#Override
public Function<A, R> finisher() {
return (A x) -> {
R res = wrapped.finisher().apply(x);
System.out.println(res);
return res;
};
}
#Override
public Set<Characteristics> characteristics() {
// Need to ensure IDENTITY_FINISH is removed, otherwise `finisher` is not called
return wrapped.characteristics().stream().filter(c -> c != IDENTITY_FINISH).collect(Collectors.toSet());
}
};
}
Both approaches print (in java 15):
{false=[Stonehenge], true=[Mount Fuji, Eiffel Tower, Statue of Liberty]}
You can add a dummy stage to the chain with a side-effect:
public class Example {
public static void main(String[] args) {
var landmarks = Set.of("Eiffel Tower", "Statue of Liberty",
"Stonehenge", "Mount Fuji");
var result = landmarks
.stream()
.collect(Collectors.partitioningBy(b -> b.contains(" ")))
.entrySet()
.stream()
.map( e -> {
System.out.println(e.getKey() + ": " + e.getValue());
return e;
})
.flatMap(t -> t.getValue().stream())
.collect(Collectors.groupingBy(s -> !s.startsWith("S")));
}
}
I am not sure if I fully understood the problem. Is this what yout are trying to achieve?
void test() {
var landmarks = Set.of("Eiffel Tower", "Statue of Liberty", "Stonehenge", "Mount Fuji");
var result1 = landmarks
.stream()
.collect(Collectors.partitioningBy(this::containsSpace))
.entrySet()
.stream()
.flatMap(t -> t.getValue().stream())
.collect(Collectors.groupingBy(s -> !s.startsWith("S")));
}
private boolean containsSpace(String in) {
var contains = in.contains(" ");
System.out.printf("%b - %s%n", contains, in);
return contains;
}
Result:
true - Statue of Liberty
true - Mount Fuji
true - Eiffel Tower
false - Stonehenge
I have the following Java loop:
for (final Long orderNumber : availableOrderIds) {
final List<ReservationDetailResponse> reservations = tryToReserve(orderNumber);
if (successful(reservations)) {
return withReservationIdFrom(reservations);
}
}
And methods:
private boolean successful(final List<ReservationDetailResponse> reservations) {
return !reservations.isEmpty();
}
private Long withReservationIdFrom(final List<ReservationDetailResponse> reservations) {
return reservations.get(0).getReservationId();
}
How do I convert it into a stream processing?
Thanks for any help!
Using map for transformation, filter for conditions and findFirst you can do somethign like:
return availableOrderIds.stream()
.map(this::tryToReserve)
.filter(this::successful)
.findFirst()
.map(this::withReservationIdFrom)
.orElse(0L); // assumed as default value
Additionally, provided the utilities, you can include them within the operations as well:
return availableOrderIds.stream()
.map(this::tryToReserve)
.filter(res -> !res.isEmpty())
.findFirst()
.map(res -> res.get(0).getReservationId())
.orElse(0L);
Something along the lines of the following:
availableOrderIds.stream()
.map(orderNumber -> tryToReserve(orderNumber))
.filter(reservation -> successful(reservation))
.map(reservations -> withReservationIdFrom(reservations))
.findFirst()
.get();
I have the following piece of code:
List<Object> list = new ArrayList<>();
list.addAll(method1());
if(list.isEmpty()) { list.addAll(method2()); }
if(list.isEmpty()) { list.addAll(method3()); }
if(list.isEmpty()) { list.addAll(method4()); }
if(list.isEmpty()) { list.addAll(method5()); }
if(list.isEmpty()) { list.addAll(method6()); }
return list;
Is there a nice way to add elements conditionally, maybe using stream operations? I would like to add elements from method2 only if the list is empty otherwise return and so on.
Edit: It's worth to mention that the methods contain heavy logic so need to be prevented from execution.
You could try to check the return value of addAll. It will return true whenever the list has been modified, so try this:
List<Object> list = new ArrayList<>();
// ret unused, otherwise it doesn't compile
boolean ret = list.addAll(method1())
|| list.addAll(method2())
|| list.addAll(method3())
|| list.addAll(method4())
|| list.addAll(method5())
|| list.addAll(method6());
return list;
Because of lazy evaluation, the first addAll operation that added at least one element will prevent the rest from bein called. I like the fact that "||" expresses the intent quite well.
I would simply use a stream of suppliers and filter on List.isEmpty:
Stream.<Supplier<List<Object>>>of(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6())
.map(Supplier<List<Object>>::get)
.filter(l -> !l.isEmpty())
.findFirst()
.ifPresent(list::addAll);
return list;
findFirst() will prevent unnecessary calls to methodN() when the first non-empty list is returned by one of the methods.
EDIT:
As remarked in comments below, if your list object is not initialized with anything else, then it makes sense to just return the result of the stream directly:
return Stream.<Supplier<List<Object>>>of(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6())
.map(Supplier<List<Object>>::get)
.filter(l -> !l.isEmpty())
.findFirst()
.orElseGet(ArrayList::new);
A way of doing it without repeating yourself is to extract a method doing it for you:
private void addIfEmpty(List<Object> targetList, Supplier<Collection<?>> supplier) {
if (targetList.isEmpty()) {
targetList.addAll(supplier.get());
}
}
And then
List<Object> list = new ArrayList<>();
addIfEmpty(list, this::method1);
addIfEmpty(list, this::method2);
addIfEmpty(list, this::method3);
addIfEmpty(list, this::method4);
addIfEmpty(list, this::method5);
addIfEmpty(list, this::method6);
return list;
Or even use a for loop:
List<Supplier<Collection<?>>> suppliers = Arrays.asList(this::method1, this::method2, ...);
List<Object> list = new ArrayList<>();
suppliers.forEach(supplier -> this.addIfEmpty(list, supplier));
Now DRY is not the most important aspect. If you think your original code is easier to read and understand, then keep it like that.
You could make your code nicer by creating the method
public void addAllIfEmpty(List<Object> list, Supplier<List<Object>> method){
if(list.isEmpty()){
list.addAll(method.get());
}
}
Then you can use it like this (I assumed your methods are not static methods, if they are you need to reference them using ClassName::method1)
List<Object> list = new ArrayList<>();
list.addAll(method1());
addAllIfEmpty(list, this::method2);
addAllIfEmpty(list, this::method3);
addAllIfEmpty(list, this::method4);
addAllIfEmpty(list, this::method5);
addAllIfEmpty(list, this::method6);
return list;
If you really want to use a Stream, you could do this
Stream.<Supplier<List<Object>>>of(this::method1, this::method2, this::method3, this::method4, this::method5, this::method6)
.collect(ArrayList::new, this::addAllIfEmpty, ArrayList::addAll);
IMO it makes it more complicated, depending on how your methods are referenced, it might be better to use a loop
You could create a method as such:
public static List<Object> lazyVersion(Supplier<List<Object>>... suppliers){
return Arrays.stream(suppliers)
.map(Supplier::get)
.filter(s -> !s.isEmpty()) // or .filter(Predicate.not(List::isEmpty)) as of JDK11
.findFirst()
.orElseGet(Collections::emptyList);
}
and then call it as follows:
lazyVersion(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6());
method name for illustration purposes only.
This question can be considered as based on java 8 nested streams
Suppose I have a Batch with Baskets with Items :
public class Batch {
private List<Basket> baskets;
}
public class Basket {
private List<Item> items;
}
public class Item {
private String property;
private int value;
}
I would like to rewrite this method with Java 8 streams.
public class SomeService {
public int findValueInBatch(Batch batch) {
for (Basket basket : batch.getBaskets()) {
for (Item item : basket.getItems()) {
if (item.getProperty().equals("someValue") {
return item.getValue();
}
}
}
return 0;
}
}
How should I do it ?
First step to where I'd like to go :
public int findValueInBatch(Batch batch) {
for (Basket basket : batch.getBaskets()) {
basket.getItems().stream()
.filter(item -> item.getProperty.equals("someValue")
.findFirst()
.get();
// there I should 'break'
}
}
Thanks a lot.
baskets.stream()
.flatMap(basket -> basket.getItems().stream())
.filter(item -> item.equals("someValue"))
.findAny()
.orElseThrow(NoSuchElementException::new);
The advantage of using findAny instead of findFirst is that findFirst doesn't work with parallel streams. Therefore, if you want to parallelize the above operation all you'll need to do is replace the stream() method with parallel()
Use flatMap to get ride of nested lists, extract each List<Item> and merge them into a Stream<Item>, it acts like all substream were merged together.
Use filter to ignore the non matching elements.
Use findFirst to get the first occurence only and stop processing
Use orElseThrow to throw an exception if no occurence of someValue were found.
Here you go
public class SomeService {
public int findValueInBatch(Batch batch) {
return batch.getBaskets().stream()
.flatMap(basket -> basket.getItems().stream())
.filter(item -> item.getProperty.equals("someValue"))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("value not found"));
}
}
To eliminate both loops you can use flatMap to produce a Stream<Item> of all the Items of all the Baskets :
return batch.getBaskets()
.stream()
.flatMap(b -> b.getItems().stream())
.filter(item -> item.getProperty.equals("someValue"))
.findFirst()
.orElse(some default value); // using .get() would throw an exception
// if no match is found
Consider the following method which returns a field if it exists or recursively calls itself until the field is found:
private Field getField(Class<?> clazz, String p) {
Optional<Field> field = Arrays.stream(clazz.getDeclaredFields())
.filter(f -> p.equals(f.getName()))
.findFirst();
return field.isPresent() ? field.get() : getField(clazz.getSuperclass(), p);
}
While this works, I thought I could shorten it to:
private Field getField(Class<?> clazz, String p) {
return Arrays.stream(clazz.getDeclaredFields())
.filter(f -> p.equals(f.getName()))
.findFirst()
.orElse(getField(clazz.getSuperclass(), p));
}
But the strange thing is that the .orElse part seems to always be called.
What am I missing here?
The arguments for a method are always evaluated before the method is called. You want orElseGet which takes a Supplier that will only be invoked if the Optional is not present:
private Field getField(Class<?> clazz, String p) {
return Arrays.stream(clazz.getDeclaredFields())
.filter(f -> p.equals(f.getName()))
.findFirst()
.orElseGet(() -> getField(clazz.getSuperclass(), p));
}