2016-01-02 9 views
6

W poniższym kodzie, gdy wzrok NumberFormatException Spośród for iteracji, struny w odpowiedniej formie zamieszczonej w strList przed pierwszym złego (tj "illegal_3") zostały pomyślnie przeanalizowane (tj. "1" i "2" zostały przeanalizowane jako liczby całkowite 1 i 2).Catching wyjątki od „strumienia()” lub 'parallelStream() traci poprawne wartości

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

Jednak przy wymianie for iteracji przez stream() lub parallelStream(), tracę 1 i 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); 
} 

Jaka jest specyfikacja przepływu sterowania wyjątków rzucanych od wewnątrz stream() lub parallelStream()?

Jak mogę uzyskać wynik intList = [1,2] (czyli ignorować te po pierwszym NumberFormatException jest wyrzucany) lub nawet lepiej intList = [1,2,4,6] (czyli ignorowanie złych z NumberFormatException) z stream() lub parallelStream()

+0

po prostu myślał o tych samych rzeczach wczoraj. +1 na dobre pytanie – Andremoniy

+3

Istnieje wiele powiązanych pytań (zbyt wiele, aby je tutaj wymienić, a niektóre z nich mogą (przynajmniej prawie) być duplikatami). Skrócona forma: specyfikacja przepływu kontrolnego jest zawsze taka sama, niezależnie od tego, czy używasz strumieni, czy nie. Jeśli nie chcesz, aby wyjątki wypłynęły i przerwały przepływ kontrolny, będziesz musiał je złapać lokalnie. BTW: Zauważ, że nawet ** jeśli ** pracowałeś wokół samego wyjątku: IIRC, wynik z 'parallelStream' może nadal być' [2,1] '.... – Marco13

Odpowiedz

7

Dlaczego nie tylko owinąć ciało lambda w try...catch?

Ponadto można filtrować null wartości po 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()); 

to daje pożądany intList = [1,2,4,6].

Edytuj: Aby zmniejszyć "ciężkość" próby/catch w lamdba, możesz dodać metodę pomocnika.

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

Albo unikać null, można zwrócić 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

Tak, to działa. Dzięki. (Myślę jednak, że blok 'try ... catch' jest zbyt * ciężki *, aby być w' ciele lambda.) – hengxin

+1

@hengxin zbyt ciężki? czemu? Nie czyni go ciężkim w żaden sposób. Błędne założenie ... – Andremoniy

+1

"Ciężki" składniowo; nic więcej. To dobra praca. – hengxin

2

Metoda nie może jednocześnie zwrócić wartość, a wyjątek. To jest niemożliwe.

Nie można oczekiwać, że collect() zwróci listę i wyrzuci wyjątek. Ponieważ jeśli zgłasza wyjątek, nie może zwrócić nowej listy.

Jeśli Twój kod do pętli był rzeczywiście podobny do kodu strumienia, to masz ten sam problem:

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

W skrócie: nie mylić „Tworzenie i powrocie nową listę”, z „biorąc listę i dodaj do niej elementy ".

+0

Istnieje work-aroung z wartością 'null' i predykatem' filter' ... – Andremoniy

1

Nie wiem, ile razy natknąłem się na sytuację, w której chciałem zignorować wyjątek NumberFormatException. Prawdopodobnie utworzyłbym osobną metodę wielokrotnego użytku do parsowania liczby całkowitej po cichu i zwracania wartości OptionalInt.

Oto klasa utils

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

Oto metoda badawcza

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

Używam org.apache.commons.lang3.math.NumberUtils:

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