Menangani pengecualian dengan aliran

10

Saya memiliki Map<String,List<String>>dan ingin mengubahnya menjadi Map<String,List<Long>>karena masing-masing Stringdalam daftar mewakili 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()))
               );

Masalah utama saya adalah masing String- masing mungkin tidak mewakili dengan benar a Long; mungkin ada beberapa masalah. Long::valueOfdapat meningkatkan pengecualian. Jika ini masalahnya, saya ingin mengembalikan nol atau kosongMap<String,List<Long>>

Karena saya ingin mengulang setelah outputpeta ini . Tetapi saya tidak dapat menerima konversi kesalahan apa pun; bahkan tidak satu pun. Adakah gagasan tentang bagaimana saya dapat mengembalikan output kosong jika String salah -> Konversi panjang?

AntonBoarf
sumber
Saya setuju dengan solusi Naman tetapi sayangnya di blok tangkap, saya gagal mengambil kunci (Entri :: getKey) yang String -> Konversi
panjangnya
Diskusi serupa di sini: String to int - kemungkinan data buruk perlu menghindari pengecualian di mana saya akhirnya memutuskan untuk melakukan pra-cek dengan regex (parseLong docs menggunakan aturan parsing yang sama & Anda mungkin ingin mengembalikan a LongStreamjika Anda berencana untuk menghapus emptyhasil)
AjahnCharles
Maaf, saya salah paham. Saya pikir Anda bermaksud mengembalikan satu entri sebagai kosong / nol; tapi sekarang saya pikir Anda maksud seluruh Peta!
AjahnCharles
1
Tidak sepenuhnya jelas apa gunanya - Anda ingin mengembalikan peta kosong jika ada kesalahan, tetapi masih mencetak "kunci" di mana kesalahan muncul di konsol? Maksud saya, informasi tentang konteks di mana pengecualian muncul biasanya diangkut ke tumpukan panggilan dalam pengecualian. Terlepas dari itu: Anda secara khusus bertanya tentang stream, tetapi saya sangat merekomendasikan untuk menghindari panggilan "kumpulkan" bersarang. Orang yang harus mempertahankannya nanti (dan ini mungkin masa depan Anda !) Akan bertanya-tanya apa yang Anda lakukan di sana. Setidaknya perkenalkan beberapa metode penolong yang diberi nama dengan benar.
Marco13

Jawaban:

4

Bagaimana dengan eksplisit catchatas pengecualian:

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();
    }
}
Naman
sumber
ok kedengarannya bagus ... tetapi dalam tangkapan (nfe) saya ingin mengambil nilai kunci tertentu (Entri :: getKey) dan String yang salah yang gagal sehingga saya bisa login secara tepat di mana ia salah. Apa itu mungkin ?
AntonBoarf
@AntonBoarf Jika Anda hanya ingin mencatat kunci, yang gagal mem-parsing String, gunakannfe.getMessage()
Naman
1
@AntonBoarf pesan pengecualian akan berisi string input yang salah bentuk. Untuk mendapatkan kunci yang bertanggung jawab, saya akan melakukan pencarian eksplisit, hanya ketika pengecualian terjadi, misalnyainput.entrySet().stream() .filter(e -> e.getValue().stream().anyMatch(s -> !new Scanner(s).hasNextLong())) .map(Map.Entry::getKey) .findAny()
Holger
@Pemegang. Terima kasih ... Kelihatannya rumit ... Saya ingin tahu apakah menggunakan standar untuk loop Java5 tidak lebih baik dalam kasus saya
AntonBoarf
@AntonBoarf baru saja mengimplementasikan keduanya dan membandingkan ...
Holger
3

Saya pribadi suka memberikan Optionalinput seputar penguraian angka:

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

Lalu, gunakan kode Anda sendiri (dan abaikan input buruk):

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

Selain itu, pertimbangkan metode pembantu untuk membuat ini lebih ringkas:

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

Kemudian Anda dapat memfilter hasil di kolektor aliran Anda:

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

Anda juga bisa menyimpan Optionalobjek kosong di daftar Anda, dan kemudian dengan membandingkan indeks mereka di yang baru List<Optional<Long>>(bukan List<Long>) dengan yang asli List<String>, Anda dapat menemukan string yang menyebabkan input yang salah. Anda juga bisa dengan mudah mencatat kegagalan iniMyClass#parseLong

Namun, jika keinginan Anda adalah untuk tidak beroperasi pada setiap masukan buruk sama sekali, maka mengelilingi seluruh aliran dalam apa yang Anda mencoba untuk menangkap (per jawaban Naman ini) adalah rute saya akan mengambil.

Penipu
sumber
2

Anda dapat membuat StringBuilderkunci for dengan pengecualian dan memeriksa apakah elenumerik seperti di bawah ini,

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

Semoga ini bisa membantu.

Code_Mode
sumber
0

Mungkin Anda dapat menulis metode pembantu yang dapat memeriksa angka dalam string dan menyaringnya dari aliran dan juga nilai-nilai nol lalu akhirnya mengumpulkan ke Peta.

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

Ini akan mengurus semuanya.

Dan gunakan ini di aliran Anda.

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