2016-01-02 1 views
6

Im folgenden Code, wenn NumberFormatException aus for Iteration fangen die Strings in geeigneter Form in strList vor der ersten schlechten (dh "illegal_3") wurden erfolgreich geparst (dh "1" und "2" wurden als Ganzzahlen geparst 1 und 2).Abfangen von Ausnahmen aus 'stream()' oder 'parallelStream()' verliert die korrekten Werte

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); 
} 

Wenn jedoch for Iteration durch stream() oder parallelStream() ersetzen, verliere ich 1 und 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); 
} 

Was ist die Spezifikation des Steuerflusses von geworfenen Ausnahmen innerhalb stream() oder parallelStream()?

Wie kann ich das Ergebnis von intList = [1,2] (dh ignorieren die, die nach dem ersten NumberFormatException geworfen wird) oder sogar besser intList = [1,2,4,6] (dh ignorieren die schlechten mit NumberFormatException) mit stream() oder parallelStream()

+0

Ich dachte gerade an gleichen Dinge gestern. +1 für eine gute Frage – Andremoniy

+3

Es gibt viele verwandte Fragen (zu viele, um sie hier aufzulisten, und einige von ihnen könnten (zumindest fast) Duplikate sein). Die Kurzform: Die Kontrollflussspezifikation ist immer gleich, unabhängig davon, ob Sie Streams verwenden oder nicht. Wenn Sie nicht möchten, dass die Ausnahmen platzen und den Kontrollfluss unterbrechen, müssen Sie sie lokal abfangen. BTW: Beachten Sie, dass sogar ** wenn ** Sie um die Ausnahme selbst gearbeitet haben: IIRC, das Ergebnis mit einem 'parallelStream' könnte immer noch '[2,1]' .... – Marco13

Antwort

7

Warum wickle nicht einfach Lambda-Körper in try...catch?

Sie können auch null Werte nach map filtern:

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()); 

Diese Sie intList = [1,2,4,6] gewünschten geben.

Edit: Um die "Schwere" eines try/catch in einem lamdba zu reduzieren, können Sie eine Hilfsmethode hinzufügen.

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()); 

Oder mit null zu vermeiden, können Sie einen 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()); 
+0

Ja, es funktioniert. Vielen Dank. (Allerdings denke ich, dass der "Versuch ... Fang" -Block zu * schwer * ist, um in 'Lambda-Körper' zu sein.) – hengxin

+1

@hengxin zu schwer? Warum? Es macht es in keiner Weise schwer. Es ist falsche Annahme ... – Andremoniy

+1

"Heavy" syntaktisch; nichts anderes. Es ist ein guter Work-around. – hengxin

2

Verfahren zurückkehren können beide keinen Wert zurück, und eine Ausnahme werfen. Das ist unmöglich.

So können Sie nicht erwarten, collect() sowohl eine Liste zurückgeben und eine Ausnahme auslösen. Da wenn eine Ausnahme ausgelöst wird, kann keine neue Liste zurückgegeben werden.

Wenn Ihr für Schleife Code tatsächlich ähnlich dem Strom Code war, würden Sie das gleiche Problem haben:

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{ 
     intList = collectToIntegers(strList); 
    } catch (NumberFormatException nfe) { 
     System.err.println(nfe.getMessage()); 
    } 

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

private List<Integer> collectToIntegers(List<String> strList) { 
    List<Integer> result = new ArrayList<>(); 
    for (String str : strList) { 
     result.add(Integer.parseInt(str)); 
    } 
    return result; 
} 

Kurz gesagt: nicht zu verwechseln „zu schaffen und eine neue Liste der Rückkehr“, mit „taking eine Liste und füge Elemente hinzu ".

+0

Es gibt Work-around mit 'null' Wert und' filter' Prädikat ... – Andremoniy

1

Ich weiß nicht, wie oft ich eine Situation, in der ich nur die NumberFormatException ignorieren wollte. Ich würde wahrscheinlich eine separate wiederverwendbare Methode erstellen, um Integer im Hintergrund zu analysieren und den OptionalInt-Wert zurückzugeben.

Hier ist die utils Klasse

public class IntUtils { 
    // ... other utility methods 

    public static OptionalInt parseInt(String s, Consumer<? super Exception> exceptionConsumer) { 
     try { 
      return OptionalInt.of(Integer.parseInt(s)); 
     } catch (NumberFormatException e) { 
      if (exceptionConsumer != null) { 
       // let the caller take the decision 
       exceptionConsumer.accept(e); 
      } else { 
       // default behavior 
       e.printStackTrace(); 
      } 
     } 

     return OptionalInt.empty(); 
    } 

    public static OptionalInt parseInt(String s) { 
     return parseInt(s, null); 
    } 
} 

ist die Testmethode

List<Integer> collect1 = strStream.map(str -> IntUtils.parseInt(str, Exception::printStackTrace)) 
      .filter(OptionalInt::isPresent) 
      .map(OptionalInt::getAsInt).collect(toList()); 

// or 
List<Integer> collect2 = strStream.map(IntUtils::parseInt) 
      .filter(OptionalInt::isPresent) 
      .map(OptionalInt::getAsInt).collect(toList()); 
+0

https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/primitives/Ints. html # tryParse-java.lang.String- –

0

I org.apache.commons.lang3.math.NumberUtils bin mit:

.mapToInt(s -> NumberUtils.toInt(s, Integer.MIN_VALUE)) 
.filter(x -> x > Integer.MIN_VALUE) 
Verwandte Themen