hengxin hengxin - 5 years ago 139
Java Question

Catching exceptions out of 'stream()' or 'parallelStream()' loses correct values

In the following code, when catching

NumberFormatException
out of
for
iteration, the strings in appropriate form appearing in
strList
before the first bad one (i.e.,
"illegal_3"
) have been parsed successfully (i.e.,
"1"
and
"2"
have been parsed as integers
1
and
2
).

public void testCaughtRuntimeExceptionOutOfIteration() {
List<String> strList = Stream.of("1", "2", "illegal_3", "4", "illegal_5", "6").collect(Collectors.toList());
List<Integer> intList = new ArrayList<>();

try{
for (String str : strList) {
intList.add(Integer.parseInt(str));
}
} catch (NumberFormatException nfe) {
System.err.println(nfe.getMessage());
}

List<Integer> expectedIntList = Stream.of(1, 2).collect(Collectors.toList());
// passed
assertEquals("The first two elements have been parsed successfully.", expectedIntList, intList);
}


However, when replacing
for
iteration by
stream()
or
parallelStream()
, I lose
1
and
2
.

public void testCaughtRuntimeExceptionOutOfStream() {
List<String> strList = Stream.of("1", "2", "illegal_3", "4", "illegal_5", "6").collect(Collectors.toList());
List<Integer> intList = new ArrayList<>();

try{
intList = strList.stream() // same with "parallelStream()"
.map(Integer::parseInt)
.collect(Collectors.toList());
} catch (NumberFormatException nfe) {
System.err.println(nfe.getMessage());
}

List<Integer> expectedIntList = Stream.of(1, 2).collect(Collectors.toList());
// failed: expected:<[1,2]>, but was:<[]>
assertEquals("The first two elements have been parsed successfully.", expectedIntList, intList);
}



What is the specification of the control flow of exceptions thrown from within
stream()
or
parallelStream()
?

How can I get the result of
intList = [1,2]
(i.e., ignore the ones after the first
NumberFormatException
is thrown) or even better
intList = [1,2,4,6]
(i.e., ignore the bad ones with
NumberFormatException
) with
stream()
or
parallelStream()


Answer Source

Why not just wrap lambda-body in try...catch?

Also you can filter null values after map:

    intList = strList.stream()// same with "parallelStream()"
            .map(x -> {
                try {
                    return Integer.parseInt(x);
                } catch (NumberFormatException nfe) {
                    System.err.println(nfe.getMessage());
                }
                return null;
            })
            .filter(x -> x!= null)
            .collect(Collectors.toList());

This will give you desired intList = [1,2,4,6].

Edit: To reduce the "heaviness" of a try/catch in a lamdba you can add a helper method.

static Integer parseIntOrNull(String s) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException nfe) {
        System.err.println(nfe.getMessage());
    }
    return null;
}

intList = strList.stream()
            .map(x -> parseIntOrNull(x))
            .filter(x -> x!= null)
            .collect(Collectors.toList());

Or to avoid using null, you can return a Stream

static Stream<Integer> parseIntStream(String s) {
    try {
        return Stream.of(Integer.parseInt(s));
    } catch (NumberFormatException nfe) {
        System.err.println(nfe.getMessage());
    }
    return Stream.empty();
}

intList = strList.stream()
            .flatMap(x -> parseIntStream(x))
            .collect(Collectors.toList());
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download