I am trying to convert a Map to another Map where the new key is simply the original key toString(). With the streams API I do this as follows:
mapMap.entrySet().stream().collect(Collectors.toMap(
(Map.Entry entry) -> entry.getKey().toString(),
(Map.Entry entry) -> entry.getValue()
));
The problem is that this doesn't preserve the internal Map type. I don't mind returning a TreeMap if the original map happens to be a HashMap, but the other way around is a problematic as sorting of the elements is removed. I've been fooling around with variations of the above code to get this done, but I don't seem to get very far. Right now, I have written it without streams, as follows:
TreeMap<String, Integer> stringMap = new TreeMap<>();
for (OriginalType t: originalMap.keySet()) {
stringMap.put(t.toString(), originalMap.get(t));
}
Can anyone put me in the right direction to do this with streams?
Thanks
There is an overload of Collectors.toMap which will allow you to specify which type of map you want returned.
mapMap.entrySet().stream().collect(Collectors.toMap(
(Map.Entry entry) -> entry.getKey().toString(),
(Map.Entry entry) -> entry.getValue(),
(val1, val2) -> { throw new RuntimeException("Not expecting duplicate keys"); },
() -> new TreeMap<>()
));
(A note about the third argument: it is intended as a function which will merge two values together that have the same key. When I don't expect these things to happen, I prefer to throw an exception.)
Related
I'm using WebFlux to pull data from two different REST endpoints, and trying to correlate some data from one stream with the other. I have Flux instances called events and egvs and for each event, I want to find the EGV with the nearest timestamp.
final Flux<Tuple2<Double,Object>> data = events
.map(e -> Tuples.of(e.getValue(),
egvs.map(egv -> Tuples.of(egv.getValue(),
Math.abs(Duration.between(e.getDisplayTime(),
egv.getDisplayTime()).toSeconds())))
.sort(Comparator.comparingLong(Tuple2::getT2))
.take(1)
.map(v -> v.getT1())));
When I send data to my Thymeleaf template, the first element of the tuple renders as a number, as I'd expect, but the second element renders as a FluxMapFuseable. It appears that the egvs.map(...) portion of the pipeline isn't executing. How do I get that part of the pipeline to execute?
UPDATE
Thanks, #Toerktumlare - your answer helped me figure out that my approach was wrong. On each iteration through the map operation, the event needs the context of the entire set of EGVs to find the one it matches with. So the working code looks like this:
final Flux<Tuple2<Double, Double>> data =
Flux.zip(events, egvs.collectList().repeat())
.map(t -> Tuples.of(
// Grab the event
t.getT1().getValue(),
// Find the EGV (from the full set of EGVs) with the closest timestamp
t.getT2().stream()
.map(egv -> Tuples.of(
egv.getValue(),
Math.abs(Duration.between(
t.getT1().getDisplayTime(),
egv.getDisplayTime()).toSeconds())))
// Sort the stream of (value, time difference) tuples and
// take the smallest time difference.
.sorted(Comparator.comparingLong(Tuple2::getT2))
.map(Tuple2::getT1)
.findFirst()
.orElse(0.)));
what i think you are doing is that you are breaking the reactive chain.
During the assembly phase reactor will call each operator backwards until it finds a producer that can start producing items and i think you are breaking that chain here:
egvs.map(egv -> Tuples.of( ..., ... )
you see egvs returns something that you need to take care of and chain on to the return of events.map
I'll give you an example:
// This works because we always return from flatMap
// we keep the chain intact
Mono.just("foobar").flatMap(f -> {
return Mono.just(f)
}.subscribe(s -> {
System.out.println(s)
});
on the other hand, this behaves differently:
Mono.just("foobar").flatMap(f -> {
Mono.just("foo").doOnSuccess(s -> { System.out.println("this will never print"); });
return Mono.just(f);
});
Because in this example you can see that we ignore to take care of the return from the inner Mono thus breaking the chain.
You havn't really disclosed what evg actually is so i wont be able to give you a full answer but you should most likely do something like this:
final Flux<Tuple2<Double,Object>> data = events
// chain on egv here instead
// and then return your full tuple object instead
.map(e -> egvs.map(egv -> Tuples.of(e.getValue(), Tuples.of(egv.getValue(), Math.abs(Duration.between(e.getDisplayTime(), egv.getDisplayTime()).toSeconds())))
.sort(Comparator.comparingLong(Tuple2::getT2))
.take(1)
.map(v -> v.getT1())));
I don't have compiler to check against atm. but i believe that is your problem at least. its a bit tricky to read your code.
The following code throws Exception when it tries to add a duplicate id. How can I modify the code to avoid duplicates?
return Optional.ofNullable(list)
.isPresent() ? (list.stream()
.collect(Collectors.toMap(ViewObject::getId, viewObject -> viewObject))) : new HashMap<>();
There is an overload of toMap which allows to specify mergeFunction:
Collectors.toMap(ViewObject::getId,
viewObject -> viewObject,
(a, b) -> a // Resolve the duplicates here
)
Additionally, Optional.ofNullable(list).isPresent() seems to be unnecessary. You can completely skip it by initializing the list if it is null. Optionals should be used in the method signature and not inside the method body.
I would like to use Optionals with forEach in my example below, and am not sure about the correct approach.
Basically the functionality is as follows:
List<Long> myList;
List<Long> myResultList;
myList = getValues_A();
if (null != myList && !myList.isEmpty())
return;
for (Long singleVal : myList) {
List<Long> tempList = getValues_B(singleVal);
if (null != tempList && !tempList.isEmpty())
myResultList.addAll(tempList);
}
So I simple retrieve some data into myList, check if there is some value returned, and if so, I use the result to again retrieve data and put it in a final result list.
My idea with Optionals:
List<Long> myList;
List<Long> myResultList;
myList = getValues_A();
if (null != myList && !myList.isEmpty())
return;
myResult.forEach(itemToCheck -> Optional
.ofNullable(getValues_B(itemToCheck))
.ifPresent(myResultList::addAll));
Questions:
The first part:
myList = getValues_A();
if (null != myList && !myList.isEmpty())
return;
Is there any way to use Java 8 Optionals instead?
I.e.
myList = getValues_A();
if (!Optional.ofNullable(myList).isPresent())
return;
But this would only check for null and not if the object was empty (for which I also want to return). Can this be extended with a size check of the object within the Stream?
Also, misusing Optional's isPresent as a nullcheck only is bad coding practise I guess. Any other ideas?
The second part:
I assume that even empty objects will be attempted to be added to myResultList? Can this be somehow prevented in a similar approach, i.e. check if size = 0 within the stream?
myResult.forEach(itemToCheck -> Optional
.ofNullable(getValues_B(itemToCheck))
.ifPresent(myResultList::addAll));
Small sidenote: I can't use isEmpty(Object object) of org.apache.commons.lang3.ObjectUtils as I'm with version < 3.9.
I also think it is worth mentioning that besides whole reusing Optional is not good thing in any possible case(with which I agree). We also see in this approach that we create empty list and then altering its state by adding new elements. I thing if we can we should always avoid such solutions. Much cleaner approach is to instantiate list with its elements while declaring.
For getting rid of first part, you can make the getValues_A() function to return an Optional or an empty list instead of null.It make no sense to make any processing with Optional in this method.
Second part written with stream :
List<Long> myResultList = myList.stream().map(singleVal -> getValues_B(singleVal)).filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList());
Each steps explained:
1. map(singleVal -> getValues_B(singleVal)) - each element of the list will be processed and you'll get a List as result for each.
2. filter(Objects::nonNull) - remove empty lists
3. flatMap(List::stream) - from stream of List<Long>,you'll obtain a stream of Long
4. collect(Collectors.toList()) - collect all resultList.
You may take advantage of the orELseGet() API of the Optional and the map/flatmap APIs of the stream to simplify your code.
List<Long> resultList = Optional.ofNullable(getValues_A())
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.flatMap(l -> Optional.ofNullable(getValues_B(l))
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull))
.collect(Collectors.toList());
I am using java8 streams to iterate two lists, In that one list contains some custom objects and another contains string.
With this, I have to call a method by passing custom object and sting as a input and then I have to get the count.
This is what I tried:
public int returnCode() {
/*int count = 0;
* list.forEach(x -> {
list2.forEach(p -> {
count+ = myDao.begin(conn, x.getCode(), p);
});
return count;
});*/
}
compiler is giving an error that count should be final.
Can anyone, give me how to do this in a better way.
What you're attempting to do is not possible as local variables accessed from a lambda must be final or effectively final i.e. any variable whose value does not change.
You're attempting to change the value of count in the lambda passed to the forEach hence the compilation error.
To replicate your exact code using the stream API, it would be:
int count = list.stream()
.limit(1)
.flatMapToInt(x -> list2.stream().mapToInt(p -> myDao.begin(conn, x.getCode(), p)))
.sum();
However, if you want to iterate over the entire sequence in list and not just the first then you can proceed with the following:
int count = list.stream()
.flatMapToInt(x -> list2.stream().mapToInt(p -> myDao.begin(conn, x.getCode(), p)))
.sum();
Lambdas mainly substitutes anonymous inner classes. Inside an anonymous inner class you can access only final local variables. Hence the same holds true with lambda expressions. Local variable is copied when JVM creates a lambda instance, hence it is counter intuitive to allow any update to them. So declaring the variable as final would solve the issue. But if you make it final you won't be able to do this, leading to another pitfall.
count+ = myDao.begin(conn, x.getCode(), p);
So your solution is not good and does not comply with lambda. So this will be a one way of doing it.
final int count = customObjects.stream()
.mapToInt(co -> strings.stream().mapToInt(s -> myDao.begin(conn, co.getCode(), s)).sum())
.sum();
I am trying to map one list to another using streams.
Some elements of the original list fail to map. That is, the mapping function may not be able to find an appropriate new value.
I want to know if any of the mappings has failed. Ideally I would also like to stop the processing once a failure happened.
What I am currently doing is:
The mapping function returns null if there's no mapped value
I filter() to remove nulls from the stream
I collect(), and then
I compare the size of the result to the size of the original list.
For example:
List<String> func(List<String> old, Map<String, String> oldToNew)
{
List<String> holger = old.stream()
.map(oldToNew::get)
.filter(Objects::nonNull)
.collect(Collectors.toList);
if (holger.size() < old.size()) {
// ... appropriate error handling code ...
}
else {
return holger;
}
}
This is not very elegant. Also, everything is processed even when the whole thing should fail.
Suggestions for a better way of doing it?
Or maybe I should ditch streams altogether and use good old loops?
There is no best solution because that heavily depends on the use case. E.g. if lookup failures are expected to be unlikely or the error handling implies throwing an exception anyway, just throwing an exception at the first failed lookup within the mapping function might indeed be a good choice. Then, no follow-up code has to care about error conditions.
Another way of handling it might be:
List<String> func(List<String> old, Map<String, String> oldToNew) {
Map<Boolean,List<String>> map=old.stream()
.map(oldToNew::get)
.collect(Collectors.partitioningBy(Objects::nonNull));
List<String> failed=map.get(false);
if(!failed.isEmpty())
throw new IllegalStateException(failed.size()+" lookups failed");
return map.get(true);
}
This can still be considered being optimized for the successful case as it collects a mostly meaningless list containing null values for the failures. But it has the point of being able to tell the number of failures (unlike using a throwing map function).
If a detailed error analysis has a high priority, you may use a solution like this:
List<String> func(List<String> old, Map<String, String> oldToNew) {
Map<Boolean,List<String>> map=old.stream()
.map(s -> new AbstractMap.SimpleImmutableEntry<>(s, oldToNew.get(s)))
.collect(Collectors.partitioningBy(e -> e.getValue()!=null,
Collectors.mapping(e -> Optional.ofNullable(e.getValue()).orElse(e.getKey()),
Collectors.toList())));
List<String> failed=map.get(false);
if(!failed.isEmpty())
throw new IllegalStateException("The following key(s) failed: "+failed);
return map.get(true);
}
It collects two meaningful lists, containing the failed keys for failed lookups and a list of successfully mapped values. Note that both lists could be returned.
You could change your filter to Objects::requireNonNull and catch a NullPointerException outside the stream