I use springboot + mongodb, and I am a beginer for webflux. I write code for CRUD. When I access delete ids in Controller, code not working because count alway return 0. Any one help me?
#ApiOperation(value = "Delete multi cities")
#DeleteMapping
public Mono<ResponseEntity<AtomicInteger>> deleteByIds(#RequestBody #NotNull Set<String> ids) {
AtomicInteger count = new AtomicInteger(0);
Flux.fromIterable(ids)
.flatMap((id) -> {
return cityService.findById(id)
.flatMap((c) -> {
count.getAndAdd(1);
return cityService.deleteById(c.getId());
});
});
log.debug("count = {}", count);
return Mono.just(ResponseEntity.ok(count));
}
The Flux is not be subscribed
you should try like this
return Flux.fromIterable(ids)
.flatMap((id) -> {
return cityService.findById(id)
.flatMap((c) -> {
count.getAndAdd(1);
return cityService.deleteById(c.getId());
});
})
.then(Mono.defer(() -> {
log.debug("count = {}", count);
return Mono.just(ResponseEntity.ok(count));
}));
class MyObject {
String key;
String value;
}
I have two lists, listA and listB containing MyObject objects.
I'm trying to convert the below nested for loops to Java8 streams based :
for(String objA : listA) {
for(String objB : listB) {
if(objA.getKey().equals(objB.getKey()) && !objA.getValue().equals(objB.getValue())) {
// create an object of a different class `MyDiff` with params as
// objA.getKey(), objA.getValue(), objB.getValue()
// and push it to a list.
}
}
}
I tried to solve this with JAVA8 as,
List<MyDiff> diff = listA.streams().filter(objA -> {
listB.stream.anyMatch(objB -> objA.getKey().equals(obj.getKey()) && !objA.getValue().equals(obj.getValue()))
})
But anyMatch doesn't return an object and I can't use filter instead of it.
You can use the flatMap() and filter() operator to complete.
List<MyDiff> diff =
listA.stream().flatMap(objA ->
listB.stream()
.filter(objB -> objA.getKey().equals(objB.getKey()) && !objA.getValue().equals(objB.getValue()))
.map(objB -> new MyDiff(objA.getKey(), objA.getValue(), objB.getValue())))
.collect(Collectors.toList());
How can I rewrite this function in a reactive way (I don't really like modifying the object within .doOnNext(:
Single<List<Integer>> getListSingle()
{
return Observable.range(0, new Random().nextInt())
.toList()
.doOnSuccess(list -> {
if (list.size() < 10) {
list.add(123);
}
});
}
The case seems a little contrived but you could always copy the immutable object in map:
Observable.range(0, new Random().nextInt())
.toList()
.map(integers -> {
if (integers.size() < 10) {
List<Integer> copy = new ArrayList<>(integers);
copy.add(123);
return copy;
} else {
return integers;
}
});
I have 3 CompletableFutures all 3 returning different data types.
I am looking to create a result object that is a composition of the result returned by all the 3 futures.
So my current working code looks like this:
public ClassD getResultClassD() {
ClassD resultClass = new ClassD();
CompletableFuture<ClassA> classAFuture = CompletableFuture.supplyAsync(() -> service.getClassA() );
CompletableFuture<ClassB> classBFuture = CompletableFuture.supplyAsync(() -> service.getClassB() );
CompletableFuture<ClassC> classCFuture = CompletableFuture.supplyAsync(() -> service.getClassC() );
CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
.thenAcceptAsync(it -> {
ClassA classA = classAFuture.join();
if (classA != null) {
resultClass.setClassA(classA);
}
ClassB classB = classBFuture.join();
if (classB != null) {
resultClass.setClassB(classB);
}
ClassC classC = classCFuture.join();
if (classC != null) {
resultClass.setClassC(classC);
}
});
return resultClass;
}
My questions are:
My assumption here is that since I am using allOf and thenAcceptAsync this call will be non blocking. Is my understanding right ?
Is this the right way to deal with multiple futures returning different result types ?
Is it right to construct ClassD object within thenAcceptAsync ?
Is it appropriate to use the join or getNow method in the thenAcceptAsync lambda ?
Your attempt is going into the right direction, but not correct. Your method getResultClassD() returns an already instantiated object of type ClassD on which an arbitrary thread will call modifying methods, without the caller of getResultClassD() noticing. This can cause race conditions, if the modifying methods are not thread safe on their own, further, the caller will never know, when the ClassD instance is actually ready for use.
A correct solution would be:
public CompletableFuture<ClassD> getResultClassD() {
CompletableFuture<ClassA> classAFuture
= CompletableFuture.supplyAsync(() -> service.getClassA() );
CompletableFuture<ClassB> classBFuture
= CompletableFuture.supplyAsync(() -> service.getClassB() );
CompletableFuture<ClassC> classCFuture
= CompletableFuture.supplyAsync(() -> service.getClassC() );
return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
.thenApplyAsync(dummy -> {
ClassD resultClass = new ClassD();
ClassA classA = classAFuture.join();
if (classA != null) {
resultClass.setClassA(classA);
}
ClassB classB = classBFuture.join();
if (classB != null) {
resultClass.setClassB(classB);
}
ClassC classC = classCFuture.join();
if (classC != null) {
resultClass.setClassC(classC);
}
return resultClass;
});
}
Now, the caller of getResultClassD() can use the returned CompletableFuture to query the progress state or chain dependent actions or use join() to retrieve the result, once the operation is completed.
To address the other questions, yes, this operation is asynchronous and the use of join() within the lambda expressions is appropriate. join was exactly created because Future.get(), which is declared to throw checked exceptions, makes the use within these lambda expressions unnecessarily hard.
Note that the null tests are only useful, if these service.getClassX() can actually return null. If one of the service calls fails with an exception, the entire operation (represented by CompletableFuture<ClassD>) will complete exceptionally.
I was going down a similar route to what #Holger was doing in his answer, but wrapping the Service Calls in an Optional, which leads to cleaner code in the thenApplyAsync stage
CompletableFuture<Optional<ClassA>> classAFuture
= CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassA())));
CompletableFuture<Optional<ClassB>> classBFuture
= CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassB()));
CompletableFuture<Optional<ClassC>> classCFuture
= CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassC()));
return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
.thenApplyAsync(dummy -> {
ClassD resultClass = new ClassD();
classAFuture.join().ifPresent(resultClass::setClassA)
classBFuture.join().ifPresent(resultClass::setClassB)
classCFuture.join().ifPresent(resultClass::setClassC)
return resultClass;
});
I ran into something similar before and created a short demo to show how I solved this issue.
Similar concept to #Holger except I used a function to combine each individual future.
https://github.com/te21wals/CompletableFuturesDemo
Essentially:
public class CombindFunctionImpl implement CombindFunction {
public ABCData combind (ClassA a, ClassB b, ClassC c) {
return new ABCData(a, b, c);
}
}
...
public class FutureProvider {
public CompletableFuture<ClassA> retrieveClassA() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new ClassA();
});
}
public CompletableFuture<ClassB> retrieveClassB() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new ClassB();
});
}
public CompletableFuture<ClassC> retrieveClassC() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new ClassC();
});
}
}
......
public static void main (String[] args){
CompletableFuture<ClassA> classAfuture = futureProvider.retrieveClassA();
CompletableFuture<ClassB> classBfuture = futureProvider.retrieveClassB();
CompletableFuture<ClassC> classCfuture = futureProvider.retrieveClassC();
System.out.println("starting completable futures ...");
long startTime = System.nanoTime();
ABCData ABCData = CompletableFuture.allOf(classAfuture, classBfuture, classCfuture)
.thenApplyAsync(ignored ->
combineFunction.combind(
classAfuture.join(),
classBfuture.join(),
classCfuture.join())
).join();
long endTime = System.nanoTime();
long duration = (endTime - startTime);
System.out.println("completable futures are complete...");
System.out.println("duration:\t" + Duration.ofNanos(duration).toString());
System.out.println("result:\t" + ABCData);
}
Another way to handle this if you don't want to declare as many variables is to use thenCombine or thenCombineAsync to chain your futures together.
public CompletableFuture<ClassD> getResultClassD()
{
return CompletableFuture.supplyAsync(ClassD::new)
.thenCombine(CompletableFuture.supplyAsync(service::getClassA), (d, a) -> {
d.setClassA(a);
return d;
})
.thenCombine(CompletableFuture.supplyAsync(service::getClassB), (d, b) -> {
d.setClassB(b);
return d;
})
.thenCombine(CompletableFuture.supplyAsync(service::getClassC), (d, c) -> {
d.setClassC(c);
return d;
});
}
The getters will still be fired off asynchronously and the results executed in order. It's basically another syntax option to get the same result.
I have a collection of data like dummy below
class Place {
userId,
price
}
That means a collection of some places.
Use-case:
There is a user with userId and login.
How to calc average place-price that equal to userId ?
RxJava is nice and I have tried filter and toList, however it is not so performance nice.
Observable.fromIterable(places)
.subscribeOn(Schedulers.newThread())
.filter(new Predicate<Place>() {
#Override
public boolean test(Place place) throws Exception {
return place.userId == global.login.userId;
}
})
.toList()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Place>>() {
#Override
public void accept(List<Place> filteredPlace) throws Exception {
//Here I have to use loop to do math-average, it is not nice to average.
}
});
If the places is something that is already available in-memory, you can rearrange the evaluation such as this:
Observable.just(places)
.subscribeOn(Schedulers.computation())
.map((Iterable<Place> list) -> {
double sum = 0.0;
int count = 0;
for (Place p : list) {
if (p.userId == global.login.userId) {
sum += p.price;
count++;
}
}
return sum / count;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(average -> { /* display average */ });
If the sequence of places becomes available over time (through an Observable):
Observable<Place> places = ...
places
.observeOn(Schedulers.computation())
.filter((Place p) -> p.userId == global.login.userId)
.compose(o -> MathObservable.averageDouble(o.map(p -> p.price)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(average -> { /* display average */ });
MathObservable is part of the RxJava 2 Extensions library.