ula.uvula
ula.uvula

Reputation: 195

Ignore exception in stream operations

Assuming you have an exception (checked/unchecked) in a stream operation and you want to ignore from now on this element. The stream must not be aborted, just ignoring elements throwing exceptions. I explicitly avoid saying skip, because it is a stream operation.

So the example is using the map() operation for demonstration. Here I have a division by zero (for example), so the "map" should skip this element.

As an example:

@Test
public void ignoreException() {
    assertThat(Stream.of(1,2,1,3).map(i -> 10 / i).reduce(0, Integer::sum), is(28));
    // the zero will break the next stream
    assertThat(Stream.of(1,2,0,3).map(i -> 10 / i).reduce(0, Integer::sum), is(18));
}

So the division by zero can break the whole stream.

I found a lot of articles that wrap a runtime exception in a checked exception (throw new RuntimeException(ex)). Or partial vs. total functions.

Or I made a wrapper returning a java.util.function.Function (e.g: ....map(wrapper(i -> 10/i))...), returning a "null" in the case of a exception. But right-hand operation may now fail, as in my example (reduce).

The only useful approach is an "EITHER" concept (a stream of EITHER), so the division by zero in my example will become a "left" and can be handled in a different way.

Upvotes: 3

Views: 3711

Answers (3)

Jose Quijada
Jose Quijada

Reputation: 690

In the catch clause generate a dummy element, and then filter it before the reduction phase,

myCollection.stream().map(e -> 
  try { 
    var res = // ... transform e
    return res;
  } catch (Exception e) { 
     return dummy;
  }
).filter(e -> e != dummy).collect(...)

Can even return null if possible, and filter on that.

Upvotes: 0

Minh Đức Tạ
Minh Đức Tạ

Reputation: 84

Try this:

@Test
public void ignoreException() {
assertThat(Stream.of(1,2,1,3).map(i -> i == 0 ? 0 : 10 / i).reduce(0, Integer::sum), is(28));
// the zero will break the next stream
assertThat(Stream.of(1,2,0,3).map(i -> i == 0 ? 0 : 10 / i).reduce(0, Integer::sum), is(18));
}

Upvotes: 0

Louis Wasserman
Louis Wasserman

Reputation: 198471

There are relatively few operations on streams that can achieve a transformation of elements and result in elements being dropped -- in fact, there's really only one, flatMap.

So your wrapper more or less has to look like

interface CanThrow<F, T> { T apply(F from) throws Exception; }
<T, R> Function<T, Stream<R>> wrapper(CanThrow<T, R> fn) {
  return t -> {
   try {
    return Stream.of(fn.apply(t));
   } catch (Exception ignored) { return Stream.empty(); }
  }
}


assertThat(Stream.of(1, 2, 0, 3).flatMap(wrapper(i -> 10 / i)).reduce(0, Integer::sum))
   .isEqualTo(18));

Upvotes: 3

Related Questions