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.
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 have the following logic in my method where I check for the value of an optional parameter, and depending on that I build another object.
AtomicReference<Employee> employeeValue = null;
questions.forEach(question -> {
if(question.isBoolean().isPresent()) {
employeeValue.set(Employee.builder()
.withBooleanValue(Boolean.valueOf(question.value()))
.build());
} else {
employeeValue.set(Employee.builder()
.withStringValue(question.value())
.build());
}
Record record = Record.builder()
.withId(question.id())
.withValue(employeeValue.get())
.build();
answers.add(record);
});
How can I replace the above with ifPresent and orElse? I'm using Java 8 and therefore ifPresentOrElse method is not available. If I am to use ifPresent and orElse separately with anonymous inner function, how do I go about it?
Any help would be much appreciated.
You neither need isPresent() nor ifPresent(). You don’t need peek() (as in the other answer) nor an AtomicReference (as in the question). I believe that this does it:
questions.forEach(question -> {
Employee empl = question.isBoolean()
.map(b -> Employee.builder()
.withBooleanValue(Boolean.valueOf(question.value()))
.build())
.orElseGet(() -> Employee.builder()
.withStringValue(question.value())
.build());
Record record = Record.builder()
.withId(question.id())
.withValue(empl)
.build();
answers.add(record);
});
You can probably apply this idea inside the stream from the other answer if you want. Rather than using Stream.forEach() I’d prefer to collect into a collection like a list and then use answers.addAll().
You can stream through questions and use peek and map-orElse construction to achieve the same result:
questions.stream()
.peek(question -> {
Employee employee = question.isBoolean()
.map(b -> Employee.builder().withBooleanValue(Boolean.valueOf(question.value())).build())
.orElse(Employee.builder().withStringValue(question.value()).build());
employeeValue.set(employee);
}
)
.map(question -> Record.builder().withId(question.id()).withValue(employeeValue.get()).build())
.forEach(answers.add(answer)); // did you mean 'record'?
But to be honest it does not change a lot - your implementation looks maybe less "java eightish" but is fine :)
I am trying to convert an iterative block of code in Java 8 to functional. The functional approach is unable to find the matching message in the set shared.
List<Optional<Message>> allMessages = new ArrayList<>();
Set<Status> allStatuses = getAllStatuses();
//Iterative : Working
Set<StatusMessage> set = new HashSet<>(STATUS_MESSAGE.values());
for (StatusMessage statusMessage : set) {
for (Status status : statusMessage.getStatusAndInfo().keySet()) {
Optional<Message> message = MessageBuilder.createMessage(allStatuses, status, this::createMessage);
if (message.isPresent()) {
allMessages.add(message);
break;
}
}
}
//Functional : Not working - Never adds anything to the
//map even when matching status is present
STATUS_MESSAGE.values().stream()
.distinct()
.map(statusMessage -> statusMessage.getStatusAndInfo().keySet())
.flatMap(Collection::stream)
.map(key -> MessageBuilder.createMessage(allStatuses, key, this::createMessage))
.anyMatch(allMessages::add);
The MessageBuilder.createMessage looks like this:
Optional<Status> matchingStatus = statuses.stream()
.filter(matchingStatus::equals)
.findFirst();
System.out.println("Found : " + matchingStatus.toString());
return matchingStatus.flatMap(creator);
Also, for debugging purposes, how can I see what is happening at each step of the stream? The stack in the debugger in intellij wasn't showing anything in the stream.
This should do it:
STATUS_MESSAGE.values().stream()
.distinct()
.forEach(statusMessage ->
statusMessage.getStatusAndInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.findFirst()
.ifPresent(allMessages::add)
);
UPDATE
To build the result list using toList instead of adding to a list:
List<Optional<Message>> allMessages = STATUS_MESSAGE.values().stream()
.distinct()
.flatMap(statusMessage ->
statusMessage.getStatusAndInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.limit(1)
)
.collect(Collectors.toList());
This should be a comment, but it's too long...
Seems like your MessageBuilder.createMessage method is overcomplicated.
Check below a simplified and more readable version of the same logic:
if (allStatuses.contains(status)) {
System.out.println("Found : " + status.toString());
return creator.apply(status);
}
return Optional.empty();
You should not use forEach for accumulating operations, so this should be more idiomatic:
Function<StatusInfo, Optional<Message>> messageForStatus = statusInfo ->
statusInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());
allMessages = STATUS_MESSAGE.values().stream()
.distinct()
.map(StatusMessage::getStatusAndInfo)
.map(messageForStatus)
.filter(Optional::isPresent)
.collect(toList());
As a side note, you have too many optionals, you may want to consider unwrapping some earlier, as a list of optionals may just as well be the list of only the present values.
I have a requirement to validate a field against some predefined values (that can grow in future). So for this I have created a Enum and defined a method that returns the stream of the allowed values.
public enum EnumDemo {
VERSION("1.0.0","2.0.3");
private List<String> ver;
EnumDemo(String... ver) {
this.ver = Arrays.asList(ver);
}
public List<String> getVer() {
return ver;
}
public static Stream<EnumDemo> stream() {
return Arrays.stream(EnumDemo.values());
}
}
Now I need to validate a field against the values defined in this Enum.
I'm using:
Optional<EnumDemo> ab = EnumDemo.stream()
.map(l -> {l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();})
.findFirst();
System.out.println(ab.get().getVer());
But it is giving me compilation error. Any help would be appreciated.
Edit:
Compilation Error:
The method map(Function<? super EnumDemo,? extends R>) in the type Stream<EnumDemo> is not applicable for the arguments ((<no type> l) -> {})
You should write it this way:
Optional<EnumDemo> ab = EnumDemo.stream().filter(l -> l.getVer().contains("2.0.3"))
.findFirst();
By the way, it wasn't working because you used {} for the lambda expression, so it was expecting a return statement in the {}. You could either remove the {} (along with the ;) or add in the return.
Anyway the original codes looked confusing, not sure if I guessed the intention correctly, but this implementation should be clearer.
Edit
Based on your comment, this is what you need:
EnumDemo.stream().flatMap(l -> l.getVer().stream())
.filter("2.0.3"::equals)
.findAny()
.ifPresent(System.out::println);
Update
Holger commented that there is a shorter and more meaningful way, with better performance:
if(EnumDemo.stream()
.anyMatch(l -> l.getVer().contains(userString))) {
System.out.println(userString);
}
To understand it, you have to think about lambdas. Lambdas represent interfaces but are specially treated by the JVM, so not every Lambda needs a class to represent. (Stateless lambdas can be just methods).
Now when looking at the map() method in the Stream interface:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
You see that it expects an implementation of the Function interface. You now have many different ways to provide that mapper. In this example lets map from Object to String:
1. Using an inline lambda:
.map(o -> o.toString())
2. Using a multiline lambda:
.map(o -> {
return o.toString();
})
3. Using method references:
.map(Object::toString)
4. Using an anonymous class:
.map(new Function<Object, String>(){
#Override
public String apply(Object o){
return o.toString();
}
})
Your current code uses the 2. approach. But without a return statement. This is even better seen when looking at the anonymous class at 4.. It seems natural, that when not using a return statement in a method that no value is returned.
And that's why you get the compilation error.
You just have to add the return statement:
.map(l -> {
return l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();
});
Or remove the brackets {}:
.map(l -> l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst());
Or even use the approach provided by #Jai in his answer. Which works even better, than what you currently have.
You are using lambda expression and not returning any value so it is giving compilation error. It is better to use ifPresent()
String val="2.0.3";
EnumDemo.stream()
.flatMap(l -> l.getVer().stream())
.filter(c -> c.equals(val))
.findAny()
.ifPresent(x -> System.out.println(x));
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())
)
);