ストリームによる例外の処理


10

リストMap<String,List<String>>Map<String,List<Long>>あるそれぞれStringがを表すので、私はそれを変えたいと思います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()))
               );

私の主な問題は、それぞれStringが正しく表示されない可能性があることLongです。何か問題があるかもしれません。Long::valueOf例外が発生する場合があります。この場合、nullまたは空を返したいMap<String,List<Long>>

このoutputマップを繰り返し処理したいからです。しかし、エラー変換を受け入れることはできません。一つでもない。不正な文字列->長い変換の場合に空の出力を返す方法に関するアイデアはありますか?


ナマンソリューションに同意しますが、残念なことに、catchブロックで、String-> Long変換が正しくないキー(Entry :: getKey)を取得できません
AntonBoarf

ここで同様の議論:文字列から整数-正規表現で事前チェ​​ックすることを最終的に決定した例外を回避するために不良データが必要になる可能性があります(parseLongドキュメントは同じ解析ルールを使用し、結果LongStreamを削除する予定がある場合はおそらく返す必要がありますempty
AjahnCharles

すみません、誤解しました。単一のエントリを空/ nullとして返すつもりだと思いました。しかし今、私はあなたがマップ全体を意味すると思います!
AjahnCharles

1
主なポイントが明確ではありません。エラーが発生した場合に空のマップを返しますが、コンソールにエラーが表示された「キー」を出力しますか?つまり、例外が発生したコンテキストに関する情報は、通常、例外のコールスタック転送さます。それとは関係なく、ストリームについて具体的に尋ねましたが、ネストされた「collect」呼び出しを避けることを強くお勧めします。後でそれを維持しなければならない人(そしてこれはあなたの将来かもしれません!)少なくとも、適切な名前のヘルパーメソッドをいくつか導入してください。
Marco13

回答:


4

catch例外の明示はどうですか:

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

いいですね... catch(nfe)で、キー(Entry :: getKey)の特定の値と、それが失敗した不正な文字列を取得したいので、どこで問題が発生したかを正確に記録できます。出来ますか ?
AntonBoarf

@AntonBoarf文字列の解析に失敗したキーのみをログに記録する場合は、nfe.getMessage()
Naman

1
@AntonBoarf例外のメッセージには、不正な形式の入力文字列が含まれます。責任あるキーを取得するには、私は、例外は、例えば起こった場合にのみ、明示的な検索を行うだろうinput.entrySet().stream() .filter(e -> e.getValue().stream().anyMatch(s -> !new Scanner(s).hasNextLong())) .map(Map.Entry::getKey) .findAny()
ホルガー

@保有者。ありがとう...それは複雑に見えます... standart for loop Java5を使用する方が私の場合は良くないのではないかと思っています
AntonBoarf

@AntonBoarfは両方を実装して比較します…
Holger

3

私は個人的にOptional、数値解析に関する入力を提供したいと思っています。

public static Optional<Long> parseLong(String input) {
    try {
        return Optional.of(Long.parseLong(input));
    } catch (NumberFormatException ex) {
        return Optional.empty();
    }
}

次に、独自のコードを使用します(不正な入力を無視します)。

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

さらに、これをより簡潔にするヘルパーメソッドを検討してください。

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

次に、ストリームのコレクタで結果をフィルタリングできます。

Map<String, List<Long>> converted = input.entrySet().stream()
    .collect(Collectors.toMap(Entry::getKey, MyClass::convertEntry));

空のOptionalオブジェクトをリストに保持して、List<Optional<Long>>(の代わりにList<Long>)新しいオブジェクトのインデックスを元のと比較することで、List<String>誤った入力の原因となった文字列を見つけることができます。これらの失敗を単にログインすることもできますMyClass#parseLong

あなたの欲求は、上で動作しないことがある場合は、任意のその後(ナマンの答えあたり)あなたがキャッチしようとしているものでストリーム全体を囲む、すべてで不正な入力、私はかかるだろうルートです。


2

あなたはStringBuilder例外でforキーを作成し、ele以下のように数値であるかどうかを確認できます、

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

それが役に立てば幸い。


0

おそらく、文字列内の数値をチェックし、ストリームからそれらを除外するヘルパーメソッドを記述して、最後にnull値をMapに収集できます。

// StringUtils.java
public static boolean isNumeric(String string) {
    try {
        Long.parseLong(string);
        return true;
    } catch(NumberFormatException e) {
        return false;
    }
}

これですべてが処理されます。

そして、これをストリームで使用してください。

Map<String, List<Long>> newMap = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> mapToLongValues(entry.getValue())));

public List<Long> mapToLongValues(List<String> strs) {
    return strs.stream()
        .filter(Objects::nonNull)
        .filter(StringUtils::isNumeric)
        .map(Long::valueOf)
        .collect(Collectors.toList());
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.