I have the following method:
public void countLetters() {
Flux.just("alpha", "bravo", "charlie")
.map(String::toUpperCase)
.flatMap(s -> Flux.fromArray(s.split("")))
.groupBy(String::toString)
.sort(Comparator.comparing(GroupedFlux::key))
.flatMap(group -> Mono.just(group.key()).and(group.count()))
.map(keyAndCount ->
keyAndCount.getT1() + " => " + keyAndCount.getT2())
.subscribe(System.out::println);
}
It gives me these errors:
Cannot resolve method 'getT1' in 'Void'
Cannot resolve method 'getT2' in 'Void'
I'm not sure what this means. Is it because my method is void or is it some other reason?
Instead of
.flatMap(group -> Mono.just(group.key()).and(group.count()))
You need
.map(group -> new Tuple2<>(group.key(), group.count()))
The issues in your flatMap:
It doesn't need to be a flat map at all. You are mapping your response in a "1 to 1" fashion without any further reactive streams, so the Mono.just isn't needed.
Mono.and joins 2 Monos and will produce a new Mono<Void> which is a mono that only completes (when all joined monos complete), meaning it has no result (doesn't emit any values). This means that .map and .flatMap on this mono will accept a Void as param (main reason for your compile time error). Not just that, but they will never be called, as Mono<Void> don't emit any Void values, they just complete (similar to Completable from RxJava).
For example, if you have
Mono<File> downloadFile = ...;
Mono<Long> calculateNumber = ...;
and you perform
Mono<Void> test = downloadFile.and(calculateNumber);
You would create a new Mono that completes when both monos complete, but throws away both values!
So:
test.map(v -> {
// v is type of "Void", and this map will never be called!
System.out.println("this will never be printed!");
return 0;
}).doFinally((signalType) -> {
System.out.println("this will be called!");
}).subscribe(
v -> System.out.println("wont be called, nothing is emitted!"),
err -> System.out.println("this might be called, if the stream emits an error"),
() -> System.out.println("this will be called, as its a completion handler!")
);
The mono test will terminate/complete after both monos joined with and complete.
If you really want a flatMap with Mono.just-ing the values of the tuple, you could use Mono.zip.
Tuple2 Cannot be used directly.
So,
Instead of
.flatMap(group -> Mono.just(group.key()).and(group.count()))
You need
.flatMap(group -> Mono.just(group.key()).zipWith(group.count()))
zipWith function will combine the result from this mono and another into a Tuple2.
Related
I have a piece of asynchronous code which contains more methods and I need to make it return CompletableFuture<List> in the end.
I need to use 2 methods:
the first method getConfigsByType() returns a Flux of type Config
the second one, which needs to be applied to every individual Config object, returns CompletableFuture of type Config.
I want to use allOf() in order to get the expected result, but I have an error and I do not know why: "no instance(s) of type variable(s) U exist so that Boolean conforms to CompletionStage". The error is at this line: .thenCompose(segmentedConfig -> finalEvents.add(segmentedConfig));
private CompletableFuture<List<Config>> getConfigs(User user) {
Queue<Config> finalEvents = new ConcurrentLinkedQueue<>();
List<CompletableFuture<Config>> completableFutureList = admin.getConfigsByType(configurationProperties.getEvents()) // returns Flux<Config>
.map(config -> {
return segmentConfig(config, user) // returns CompletableFuture<Config>
.thenCompose(segmentedConfig -> finalEvents.add(segmentedConfig));
})
.collect(Collectors.toList());
return allOf(completableFutureList)
.thenApply(list -> finalEvents);
private CompletableFuture<Void> allOf(List<CompletableFuture<Config>> futuresList) {
return CompletableFuture.allOf(futuresList.toArray(new CompletableFuture[0]));
}
private CompletableFuture<Config> segmentConfig(Config config, User user) {
return configurationApi.getSegmentedConfig(new DefaultCombinedConfigProvider<>(config), user);
}
What am I doing wrong?
You can not produce the list of results before the future created by allOf(completableFutureList) has been completed. Further, a Queue<Config> won’t become a List<Config>.
So, remove your attempt to produce the result list from the stream operation that produces the List<CompletableFuture<Config>>. Then, add an actual operation producing the result list to allOf(completableFutureList).
private CompletableFuture<List<Config>> getConfigs(User user) {
List<CompletableFuture<Config>> completableFutureList
= admin.getConfigsByType(configurationProperties.getEvents())
.map(config -> segmentConfig(config, user))
.collect(Collectors.toList());
return CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[0]))
.thenApply(voidArg -> completableFutureList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
I want to map a list of tasks to List<Callable<Tasks>>:
List<Callable<Tasks>> callableTasks = tasks.stream().map(t ->
{
// how to return callable directly and omitting curly braces?
Callable<Task> task = () -> run(t);
return task;
}).collect(Collectors.toList());
How can I shorten the above expression to return the callable directly?
You can use static methods from Executors class, for example Executors.callable:
tasks.stream().map(t -> callable(() -> run(t))).collect(Collectors.toList());
The problem is that the lambda expression () -> run(t) requires a target type.
In a construct like
List<Callable<Tasks>> callableTasks = tasks.stream()
.map(…).collect(Collectors.toList());
The assignment to List<Callable<Tasks>> callableTasks provides a target type to the collect method invocation, but can’t propagate it to the preceding map invocation (a general limitation of Java’s current type inference).
By splitting the mapping function into an assignment and a return statement, you are providing a target type. Likewise, you could provide target type by casting, e.g. .map(t -> (Callable<Task>)() -> run(t)) or by providing an explicit type for the generic method map, e.g. .<Callable<Task>>map(t -> () -> run(t)).
The latter solution leads to
List<Callable<Task>> callableTasks = tasks.stream()
.<Callable<Task>>map(t -> () -> run(t)).collect(Collectors.toList());
If the Task instance returned by run(t) is the same as passed to it as argument, you can use Executors.callable like:
List<Callable<Task>> callableTasks = tasks.stream()
.map(t -> Executors.callable(() -> run(t), t)).collect(Collectors.toList());
Note the Executors.callable encapsulates a Runnable, which provides no return value. So the constructed Callable will evaluate to the result specified to Executors.callable as second argument or to null if you use the one argument version.
I have this code
public Stream<PaymentEntity> getPayments(List<String> paymentIds) {
return paymentIds.stream()
.flatMap(id -> paymentsRepository.getById(id))
}
paymentsRepository.getById(id) is returning Single<PaymentEntity>
But i got compile time error
no instance(s) of type variable(s) R exist so that
Single conforms to Stream
Edit, to anyone asking about Single http://reactivex.io/documentation/single.html
You can use
return paymentIds.stream()
.map(id -> paymentsRepository.getById(id).toBlocking().value());
Or in rxjava 2 you can use :
return paymentIds.stream()
.map(id -> paymentsRepository.getById(id).blockingGet());
After comment I think you can go with this solution :
List<PaymentEntity> result = new ArrayList<>();
paymentIds.forEach(id -> paymentsRepository.getById(id).toObservable().subscribe(result::add));
return result.stream();
Alternate solution:
public Stream<PaymentEntity> getPayments(List<String> paymentIds) {
return Observable.fromIterable(paymentIds)
.flatMapSingle(id -> paymentsRepository.getById(id))
.toList()
.blockingGet()
.stream();
}
You can wait until the current Single in the lambda signals a success value using .blockingGet():
return paymentIds.stream()
.map(id -> paymentsRepository.getById(id).blockingGet());
Or you can wait until all the reactive chain signals a success value in this way:
return Observable.fromIterable(paymentIds)
.flatMapSingle(paymentsRepository::getById)
.toList()
.blockingGet()
.stream();
Consider also to use Flowable: unlike Observable, it supports backpressure strategy.
I want to use Context in my Flux pipe to bypass filtering.
Here's what I have:
public Flux<Bar> realtime(Flux<OHLCIntf> ohlcIntfFlux) {
return Flux.zip(
ohlcIntfFlux,
ohlcIntfFlux.skip(1),
Mono.subscriberContext().map(c -> c.getOrDefault("isRealtime", false))
)
.filter(l ->
l.getT3() ||
(!l.getT2().getEndTimeStr().equals(l.getT1().getEndTimeStr())))
.map(Tuple2::getT1)
.log()
.map(this::
}
which is input to this this:
public void setRealtime(Flux<Bar> input) {
Flux.zip(input, Mono.subscriberContext())
.doOnComplete(() -> {
...
})
.doOnNext(t -> {
...
})
.subscribe()
}
I can tell my code in ... is not failing, I can even access the Context map, but when the first iteration completes, I get:
onContextUpdate(Context1{reactor.onNextError.localStrategy=reactor.core.publisher.OnNextFailureStrategy$ResumeStrategy#35d5ac51})
and subscriber disconnects.
So my question is whether I am using it right and what can be an issue here?
EDIT:
I have tried to repeat() the Mono.subscriberContext() when I'm using value out of it:
return Flux.zip(
ohlcIntfFlux,
ohlcIntfFlux.skip(1),
Mono.subscriberContext()
.map(c -> c.getOrDefault("isRealtime", new AtomicBoolean())).repeat()
)
.filter(l ->
l.getT3().get() ||
(!l.getT2().getEndTime().isEqual(l.getT1().getEndTime())))
.map(Tuple2::getT1)
and set the AtomicBoolean to the context on the subscriber end and just change the value inside this variable reference, when I need the signal on the upstream, but it doesn't change at all:
input
.onErrorContinue((throwable, o) -> throwable.getMessage())
.doOnComplete(() -> {
System.out.println("Number of trades for the strategy: " + tradingRecord.getTradeCount());
// Analysis
System.out.println("Total profit for the strategy: " + new TotalProfitCriterion().calculate(timeSeries, tradingRecord));
})
.doOnNext(this::defaultRealtimeEvaluator)
.subscriberContext(Context.of("isRealtime", isRealtimeAtomic))
.subscribe();
at least with repeat the Flux doesn't disconnect but the value I'm getting out of it is not being updated. No other clues I have.
Spring-webflux: 2.1.3.RELEASE
this works:
input
.onErrorContinue((throwable, o) -> throwable.getMessage())
.doOnComplete(() -> { ... }
.flatMap(bar -> Mono.subscriberContext()
.map(c -> Tuples.of(bar, c)))
.doOnNext(this::defaultRealtimeEvaluator)
.subscriberContext(Context.of("isRealtime", new AtomicBoolean()))
.subscribe();
so the point is to set AtomicBoolean in my case as the cotnext and then extract this variable out of the context if you want to change it's value. the same on the upstream flux.
I'm in a bit of confusion right now, so I have a method that should return CompletableFuture<List<A>>
inside the method is:
CompletableFuture<List<String>> toReturn = asyncCall().thenApply(....)
.thenCompose(listOfStuff -> convertToList(listOfStuff.stream().map(
key -> asyncCall2(key)
.thenApply(optionalValue -> optionalValue.orElse(null))
).collect(Collectors.toList()));
and convertToList() simply joins futures to convert CompletableFuture<List<ComputableFuture<A>>> into CompletableFuture<List<A>>
Basically my intention is to filter null values that emerge from optionalValue.orElse(null) And it would be easy to do filter before collecting it all to list in the last line, but if I use it just before .collect it is working over CompletableFutures
I suspect there's a lot of restructuring I can do in my code.
EDIT:
private<T> CompletableFuture<List<T>> convertToList(List<CompletableFuture<T>> toConvert) {
return CompletableFuture.allOf(toConvert.toArray(new CompletableFuture[toConvert.size()]))
.thenApply(v -> toConvert.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
The best way would probably be to change convertToList() so that it does not return a future of list, but of stream instead:
private <T> CompletableFuture<Stream<T>> convertToFutureOfStream(List<CompletableFuture<T>> toConvert) {
return CompletableFuture.allOf(toConvert.stream().toArray(CompletableFuture[]::new))
.thenApply(
v -> toConvert.stream()
.map(CompletableFuture::join)
);
}
This will be more reusable as the method will allow better chaining and will not force the caller to work with a list, while still allowing to easily get a list with a simple collect.
You can then simply filter that stream to remove empty optionals:
CompletableFuture<List<String>> toReturn = asyncCall()
.thenCompose(listOfStuff -> convertToFutureOfStream(
listOfStuff.stream()
.map(this::asyncCall2)
.collect(Collectors.toList())
)
.thenApply(stream ->
stream.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
)
);
You can even improve this a little further by changing convertToFutureOfStream() to take a stream as argument as well:
private <T> CompletableFuture<Stream<T>> convertToFutureOfStream(Stream<CompletableFuture<T>> stream) {
CompletableFuture<T>[] futures = stream.toArray(CompletableFuture[]::new);
return CompletableFuture.allOf(futures)
.thenApply(v -> Arrays.stream(futures).map(CompletableFuture::join));
}
(unfortunately this raises an unchecked assignment warning because of the array of generic types)
Which then gives
CompletableFuture<List<String>> toReturn = asyncCall()
.thenCompose(listOfStuff -> convertToFutureOfStream(
listOfStuff.stream().map(this::asyncCall2)
)
.thenApply(stream ->
stream.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
)
);