How to store intermediate state in a Java stream processing - java

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

Related

JAVA8 Optional and Lambdas

Suppose I have this class model hierarchy:
public class A {
private Integer id;
private List<B> b;
}
And:
public class B {
private Integer id;
private List<C> c;
}
And finally:
public class C {
private Integer id;
}
And a simple Service:
#Service
public class doSome {
public void test() {
Optional<A> a = Optional.of(a) // Suppose a is an instance with full hierarchy contains values
/** *1 **/ // what I want to do
}
}
Now what I want to do at the *1 position is to use lambda to extract the Optional value (if exixsts) and map the subrelation to obtain all id of the C class. I have tried something like this:
public void test() {
Optional<A> a = Optional.of(a);
List<Integer> temp = a.get().getB()
.stream()
.map(x -> x.getC())
.flatMap(List::stream)
.map(y -> y.getId())
.collect(Collectors.toList()); // works
}
Now I would like to put inside my lambda the a.get().getB(), I have tried several ways but with no luck.
Anyway I don't understand why I can't use two consecutive map like
.map(x -> x.getC())
.flatMap(List::stream)
.map(y -> y.getId())
without using flatMap(List::stream) in the middle... the map doesn't return a new Stream of Type R (class C in this case)? Why I have to Stream it again? where am I wrong?
----------------------- UPDATE ------------------
This is just an example, It's pretty clear that the Optional here is useless but in real case could comes by a findById() JPA Query.
Holger for this reasons I would put all inside a part of code, doing something like:
public <T> T findSome(Integer id) {
Optional<T> opt = repository.findById(id);
return opt.map(opt -> opt).orElse(null);
}
I have read here some solution like follows:
Optional.ofNullable(MyObject.getPeople())
.map(people -> people
.stream()
.filter(person -> person.getName().equals("test1"))
.findFirst()
.map(person -> person.getId()))
.orElse(null);
And I would like to adapt at my case but without no luck.
As of java-9 and newer, you can call Optional#stream:
List<Integer> temp = a.map(A::getB)
.stream()
.flatMap(Collection::stream)
.map(B::getC)
.flatMap(Collection::stream)
.map(C::getId)
.collect(Collectors.toList());
If you are stuck with java-8, you need to map to Stream (or return the empty one) and continue chaining:
List<Integer> temp = a.map(A::getB)
.map(Collection::stream)
.orElse(Stream.empty())
.map(B::getC)
.flatMap(Collection::stream)
.map(C::getId)
.collect(Collectors.toList());
Note: Optional<A> a = Optional.of(a) is not valid as a is already defined.

Defaulting Optional orElse with Optional.empty in Java 8

Java 8 here. I need to search two lists of POJOs for a string and want to use the Stream/Optional APIs correctly.
If the name appears in the first list ("lunches") then I want to return an optional containing it. Else, if the name appears in the second list ("dinners") then I want to return an optional containing it. Otherwise I want to return Optional.empty() if the name doesn't existing in either list. My best attempt thus far:
public class Restaurant {
private String id;
private String name;
private List<Food> lunches;
private List<Food> dinners;
public Optional<Food> findFoodByName(String name) {
return Optional.of(lunches.stream()
.filter(food -> food.getName()
.equalsIgnoreCase(name))
.findFirst())
.orElse(dinners.stream()
.filter(food -> food.getName()
.equalsIgnoreCase(name))
.findFirst());
// .orElse(null); TODO: how to return empty optional if neither in 'lunches' nor 'dinners'?
}
}
Can anyone help me cross the finish line here?
Combine both the list using Stream.of and check for element or return Optional.empty()
Stream.of(lunches, dinners)
.flatMap(List::stream)
.filter(s -> s.getName()
.equalsIgnoreCase(name))
.findFirst();
As per the suggestion from #Holger you can also use Stream.concat to concat two streams and then check for element
Stream.concat(lunches.stream(), dinners.stream())
.filter(s -> s.getName()
.equalsIgnoreCase(name))
.findFirst();
You can do like this too:
Optional<Food> firstTry = lunches.stream()
.filter(f -> f.getName().equalsIgnoreCase(name))
.findFirst();
return firstTry.map(Optional::of)
.orElse(dinners.stream()
.filter(f -> f.getName().equalsIgnoreCase(name)).findFirst());
Or in Java9
firstTry.or(() -> dinners.stream().filter(s -> s.equalsIgnoreCase(name)).findFirst());
As #Slaw commented correctly use of orElseGet() avoid eagerly computing.
Optional<Food> firstTry = lunches.stream().filter(...)...findFirst();
Supplier<Optional<Food>> secondTry = () -> dinners.stream()...findFirst();
and at the end
return firstTry.map(Optional::of).orElseGet(secondTry);

How to rewrite it using stream api

I don't know all capabilities of Stream API.
My task is: I have a list of strings with urls and I have another list of my custom objects with two methods
String videoFromUrl(String url);
boolean support(String url);
I should to choose an url from first list which will be supported by one instance of second list then return converted url
My code is:
#Override
protected String videoSourceFromDocument(final Document document) {
final List<String> hrefs = ...;
for (final String href : hrefs) {
final Optional<VideoDownloader> videoDownloader = this.videoDownloaders/*this is my second list*/
.stream()
.filter(dwnldr->dwnldr.support(href))
.findFirst();
if(videoDownloader.isPresent()){
return videoDownloader.get().videoFromUrl(href);
}
}
this.logger().warn("Url {} doesn't has video source",document.baseUri());
throw new IllegalArgumentException();
}
Is it better way to rewrite it using Stream API?
When you need two inner loops to do something in an imperative way, the solution to do the same thing using streams is usually to use a flatMap:
protected String videoSourceFromDocument(final Document document) {
final List<String> hrefs = ...;
return hrefs.stream()
.flatMap(href -> this.videoDownloaders.stream()
.filter(d -> d.support(href))
.map(d -> d.videoFromUrl(href)))
.findFirst()
.orElseThrow(() -> {
this.logger().warn("Url {} doesn't has video source", document.baseUri());
return new IllegalArgumentException();
});
}
I would remove the log, though, and put the informative message in the IllegalArgumentException. Or simply return an Optional<String>, so that the caller can decide what to do when there is no video source.
You can replace the for loop with a Stream pipeline using some Optional methods:
return hrefs.stream() // Stream<String>
.map(href -> this.videoDownloaders
.stream() // Stream<VideoDownloader>
.filter(dwnldr->dwnldr.support(href))
.findFirst() // Optional<VideoDownloader>
.map(dwnldr -> dwnldr.videoFromUrl(href))) // Stream<Optional<String>>
.filter(Optional::isPresent) // keep only the non-empty Optionals
.findFirst() // Optional<Optional<String>>
.orElseThrow(IllegalArgumentException::new) // Optional<String>
.get(); // String

Java 8 nested streams : return a value in last stream

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

method ref from lambda

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

Categories

Resources