Zip reactive flow with itself - java

I'm using Java Reactor Core, and I have a reactive Flux of objects. For each object of the Flux I need to do an external query that will return one different object for each input. The newly generated Flux needs then to be zipped with the original one - so the items of the 2 Flux must be synchronized and generated in the same order.
I'm just re-using the same flow twice, like this:
Flux<MyObj> aTest = Flux.fromIterable(aListOfObj);
Flux<String> myObjLists = aTest.map(o -> MyRepository.findById(o.name)).map(o -> {
if (!o.isPresent()) {
System.out.println("Fallback to empty-object");
return "";
}
List<String> l = o.get();
if (l.size() > 1) {
System.out.println("that's bad");
}
return l.get(0);
});
Flux.zip(aTest, myObjLists, (a, b) -> doSomethingWith(a,b))
Is it the right way to do it? If the myObjLists emits an error, how do I prevent the zip phase to skip the failing iteration?

I've finally opted for using Tuples and Optionals (to prevent null-items that would break the flux), so that I don't need to re-use the initial Flux:
Flux<Tuple<MyObj, Optional<String>>> myObjLists = Flux.fromIterable(aListOfObj)
.map(o -> Tuples.of(o, Optional.ofNullable(MyRepository.findById(o.name))
.flatMap(t -> {
if (!t.getT2().isPresent()) {
System.out.println("Discarding this item");
return Flux.empty();
}
List<String> l = t.getT2().get();
if (l.size() > 1) {
System.out.println("that's bad");
}
return Tuples.of(t.getT1(), l.get(0));
})
.map(t -> doSomethingWith(t.getT1(),t.getT2()))
Note that the flatMap could be replaced with a .map().filter(), removing tuples with missing Optional items

Related

How to run Either values by using CompletableFuture?

Java version : 11
I have a List, which contains many sublist and for each sublist I want to perform certain transformation/operations.
I want to perform this operation in non-blocking asynchronous fashion, so I am using CompletableFuture.
This is my operation:
public static List<String> convertBusinessObjectJson(List<BusinessObject> businessObjList) {
List<Either> eitherValueOrException = {//omitted logic to convert to json}
return eitherValueOrException;
}
It returns a List of Either Objects, where Either holds, either runtime exception thrown by conversion logic or String result when conversion is successful.
This is my caller code:
mainList.forEach(sublist -> {
CompletableFuture<List<Either>> listCompletableFuture = CompletableFuture.supplyAsync(() -> FutureImpl.convertBusinessObjectJson(sublist));
});
Once the CompletableFuture<List<Either>> listCompletableFuture is received, I want to chain the operation,
As in
take CompletableFuture<List<Either>> listCompletableFuture, take exceptions only from list and, perform certain operation
take CompletableFuture<List<Either>> listCompletableFuture, take results only from list and, perform certain operation
Something like this (pseudo code):
mainList.forEach(sublist -> {
CompletableFuture<List<Either>> listCompletableFuture = CompletableFuture.supplyAsync(() -> FutureImpl.convertDSRowToJson(subDSRowList));
listCompletableFuture.thenApply(//function which pushes exception to say kafka)
listCompletableFuture.thenApply(//function which pushes result to say database)
});
Can it be done?
Any help is much appreciated :)
You could try smth like this:
var futureList = mainList.stream()
.map(sublist -> CompletableFuture.supplyAsync(() -> FutureImpl.convertBusinessObjectJson(sublist)))
.collect(Collectors.toList());
The above would collect a list of CompletableFutures. Now what needs to happen is we need to wait for the completion of all those futures. We do this by:
var joinedFutureList = futureList.stream()
.map(objectCompletableFuture -> {
try {
return objectCompletableFuture.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
});
After that the separation would look smth like this:
var exceptionList = joinedFutureList.stream()
.filter(obj -> obj instanceof Exception)
.peek(System.out::println)
.collect(Collectors.toList());
var successList = joinedFutureList.stream()
.filter(obj -> obj instanceof String)
.peek(System.out::println)
.collect(Collectors.toList());

How to store intermediate state in a Java stream processing

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

Java 8 list processing - add elements conditionally

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.

How to do filter and map without overhead of repeating operation

I have some cases where using Java 8 Stream makes me repeat the execution of some operation where it could be avoided if done without the Stream, but I think that the problem is not with the stream, but me.
Some example:
private class Item {
String id;
List<String> strings;
}
// This method, filters only the Items that have the strToFind, and
// then maps it to a new string, that has the id and the str found
private void doIt(List<Item> items, String strToFind) {
items.stream().filter(item -> {
return item.strings.stream().anyMatch(str -> this.operation(str, strToFind));
}).map(item -> {
return item.id + "-" + item.strings.stream()
.filter(str -> this.operation(str, strToFind)).findAny().get();
});
}
// This operation can have a lot of overhead, therefore
// it would be really bad to apply it twice
private boolean operation(String str, String strToFind) {
return str.equals(strToFind);
}
As you can see, the function operation is being called twice for each item, and I don't want that. What I thought first was to map directly and return "null" if not found and then filter nulls, but if I do that, I will lose the reference to the Item and therefore, can't use the id.
I think you might want this behaviour :
items.stream().map(item -> {
Optional<String> optional = item.strings.stream().filter(string -> operation(string, strToFind)).findAny();
if(optional.isPresent()){
return item.id + "-" + optional.get();
}
return null;
}).filter(e -> e != null);
EDIT :
Because you're losing the information obtained in the filter when you're doing the map afterwards, but nothing prevents you from doing the operation in the map only and filtering afterwards.
EDIT 2 :
As #Jorn Vernee pointed out, you can shorten it further :
private void doIt(List<Item> items, String strToFind) {
items.stream().map(item -> item.strings.stream().filter(string -> operation(string, strToFind)).findAny()
.map(found -> item.id + "-" + found).orElse(null)).filter(e -> e != null);
}
You can use
private void doIt(List<Item> items, String strToFind) {
items.stream()
.flatMap(item -> item.strings.stream().unordered()
.filter(str -> this.operation(str, strToFind)).limit(1)
.map(string -> item.id + "-" + string))
// example terminal operation
.forEach(System.out::println);
}
The .unordered() and .limit(1) exist to produce the same behavior like anyMatch() and findAny() of your original code. Of course, .unordered() is not required to get a correct result.
In Java 9, you could also use
private void doIt(List<Item> items, String strToFind) {
items.stream()
.flatMap(item -> item.strings.stream()
.filter(str -> this.operation(str, strToFind))
.map(string -> item.id + "-" + string).findAny().stream())
// example terminal operation
.forEach(System.out::println);
}
keeping the findAny() operation, but unfortunately, Java 8 lacks the Optional.stream() method and trying to emulate it would create code less readable than the limit(1) approach.
Although not the shortest code (but this has not been asked for) I believe this works quite straightforward using Optional but does not involve any null mappings and/or checks and type information (String vs. Object) is not accidentally lost:
items.stream()
.map(item -> item.strings.stream()
.filter(str -> this.operation(str, strToFind))
.findAny()
.<String>map(string -> item.id + "-" + string))
.filter(Optional::isPresent)
.map(Optional::get);
It's pretty much a combination of Jeremy Grand's and Holger's answers.

Java streams .orElseThrow

I want convert a piece of code from a Connection Pool project i have been working on to use streams
the original code is
for (Map.Entry<JdbConnection,Instant> entry : borrowed.entrySet()) {
Instant leaseTime = entry.getValue();
JdbConnection jdbConnection = entry.getKey();
Duration timeElapsed = Duration.between(leaseTime, Instant.now());
if (timeElapsed.toMillis() > leaseTimeInMillis) {
//expired, let's close it and remove it from the map
jdbConnection.close();
borrowed.remove(jdbConnection);
//create a new one, mark it as borrowed and give it to the client
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
}
}
throw new ConnectionPoolException("No connections available");
I have got to the point of this
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now()).toMillis() > leaseTimeInMillis)
.findFirst()
.ifPresent(entry -> {
entry.getKey().close();
borrowed.remove(entry.getKey());
});
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
The above can compile but the moment i add orElseThrow after IfPresent i am getting the following
/home/prakashs/connection_pool/src/main/java/com/spakai/ConnectionPool.java:83: error: void cannot be dereferenced
.orElseThrow(ConnectionPoolException::new);
That's because ifPresent returns void. It can't be chained. You could do something like:
Entry<JdbConnection, Instant> entry =
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now())
.toMillis() > leaseTimeInMillis)
.findFirst()
.orElseThrow(ConnectionPoolException::new));
entry.getKey().close();
borrowed.remove(entry.getKey());
What you were looking for would read well:
.findFirst().ifPresent(value -> use(value)).orElseThrow(Exception::new);
But for it to work, ifPresent would have to return the Optional, which would be a little odd. It would mean you could chain one ifPresent after another, doing multiple operations on the value. That might have been a good design, but it isn't the one the creators of Optional went with.
Use map instead of isPresent, and return with an Optional instead of an exception.
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now()).toMillis() > leaseTimeInMillis)
.findFirst()
.map(entry -> {
entry.getKey().close();
borrowed.remove(entry.getKey());
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
})

Categories

Resources