I have a Map<String,List<String>>
and want it to turn into Map<String,List<Long>>
because each String
in the list represents a Long
:
Map<String,List<String>> input = ...;
Map<String,List<Long>> output=
input.entrySet()
.stream()
.collect(toMap(Entry::getKey, e -> e.getValue().stream()
.map(Long::valueOf)
.collect(toList()))
);
My main issue is each String
may not represent correctly a Long
; there may be some issue. Long::valueOf
may raise exceptions. If this is the case, I want to return a null or empty Map<String,List<Long>>
Because I want to iterate after over this output
map. But I cannot accept any error conversion; not even a single one. Any idea as to how I can return an empty output in case of incorrect String -> Long conversion?
From a stream processing, we can throw a RuntimeException. It is meant to be used if there is a real problem, the stream processing is stopped; Or if we don't want to stop the whole processing, we only need to throw a caught Exception. Then it has to be handled within the stream.
There are three types of exception—the checked exception, the error and the runtime exception.
Uncaught exception handler will be used to demonstrate the use of exception with thread. It is a specific interface provided by Java to handle exception in the thread run method. There are two methods to create a thread: Extend the thread Class (java.
Here you’ll see three primary ways to handle exceptions in a stream pipeline and each approach will include several examples. Imagine you have a collection of integers, and you want to divide each of them by a constant. A scaling operation like that can be expressed using a stream as in Example 1. Example 1.
This approach uses an utility method from the Apache Commons Lang library to throw checked exceptions in Java Streams. Hence, you need to have a dependency on this library for this. The Apache Commons Lang has an utility class called Streams which provides functions for working with Java streams.
The only problem left is that when an exception occurs, the processing of your stream stops immediately. If that is no problem for you, then go for it. I can imagine, however, that direct termination is not ideal in many situations. When working with streams, we probably don't want to stop processing the stream if an exception occurs.
Anyway, in both cases, if you use the Try or the Either, you solve the initial problem of exception handling and do not let your stream terminate because of a RuntimeException. Both the Either and the Try are very easy to implement yourself. On the other hand, you can also take a look at functional libraries that are available.
I personally like to provide an Optional
input around number parsing:
public static Optional<Long> parseLong(String input) {
try {
return Optional.of(Long.parseLong(input));
} catch (NumberFormatException ex) {
return Optional.empty();
}
}
Then, using your own code (and ignoring bad input):
Map<String,List<String>> input = ...;
Map<String,List<Long>> output=
input.entrySet()
.stream()
.collect(toMap(Entry::getKey, e -> e.getValue().stream()
.map(MyClass::parseLong)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList()))
);
Additionally, consider a helper method to make this more succinct:
public static List<Long> convertList(List<String> input) {
return input.stream()
.map(MyClass::parseLong).filter(Optional::isPresent).map(Optional::get)
.collect(Collectors.toList());
}
public static List<Long> convertEntry(Map.Entry<String, List<String>> entry) {
return MyClass.convertList(entry.getValue());
}
Then you can filter the results in your stream's collector:
Map<String, List<Long>> converted = input.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, MyClass::convertEntry));
You could also keep the empty Optional
objects in your lists, and then by comparing their index in the new List<Optional<Long>>
(instead of List<Long>
) with the original List<String>
, you can find the string which caused any erroneous inputs. You could also simply log these failures in MyClass#parseLong
However, if your desire is to not operate on any bad input at all, then surrounding the entire stream in what you're attempting to catch (per Naman's answer) is the route I would take.
How about an explicit catch
over the exception:
private Map<String, List<Long>> transformInput(Map<String, List<String>> input) {
try {
return input.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
.map(Long::valueOf)
.collect(Collectors.toList())));
} catch (NumberFormatException nfe) {
// log the cause
return Collections.emptyMap();
}
}
You can create a StringBuilder
for key with exception and check if ele
is numeric as below,
public static Map<String, List<Long>> transformInput(Map<String, List<String>> input) {
StringBuilder sb = new StringBuilder();
try {
return input.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
.map(ele->{
if (!StringUtils.isNumeric(ele)) {
sb.append(e.getKey()); //add exception key
throw new NumberFormatException();
}
return Long.valueOf(ele);
})
.collect(Collectors.toList())));
} catch (NumberFormatException nfe) {
System.out.println("Exception key "+sb);
return Collections.emptyMap();
}
}
Hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With