Akışlarla istisnaları işleme


10

Ben bir Map<String,List<String>>ve listeden Map<String,List<Long>>her biri Stringbir temsil çünkü dönüşmesini istiyorum 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()))
               );

Benim asıl meselem her Stringbir doğru temsil olmayabilir Long; bazı sorunlar olabilir. Long::valueOfistisnalar doğurabilir. Bu durumda boş veya boş değer döndürmek istiyorumMap<String,List<Long>>

Çünkü bu outputharitadan sonra tekrarlamak istiyorum . Ancak hata dönüştürme kabul edemiyorum; tek bir tane bile değil. Herhangi bir fikir nasıl yanlış String durumunda boş bir çıkış dönebilirim -> Uzun dönüşüm?


Naman çözümü ile hemfikirim ama maalesef catch bloğunda, String -> Long dönüşümünün yanlış olduğu anahtarı (Entry :: getKey)
alamıyorum

Burada benzer tartışma: Dize - muhtemelen bozuk veri sonunda regex (parseLong dokümanlar aynı ayrıştırma kurallarını kullanın ve muhtemelen sonuçları LongStreamkaldırmak isterseniz bir dönmek istiyorum empty)
istisnalardan kaçınmak gerekir

Üzgünüm, yanlış anladım. Tek bir girişi boş / boş olarak döndürmek istediğinizi düşündüm; ama şimdi sanırım bütün haritayı kastediyorsun!
AjahnCharles

1
Ana noktanın ne olduğu tamamen açık değil - bir hata durumunda boş bir harita döndürmek istiyorsunuz, ancak yine de hatanın konsolda göründüğü "anahtarı" yazdırmak mı istiyorsunuz? Ben göründü istisna genellikle çağrı yığını taşınır Bağlam hakkında bilgi anlamına içinde istisna. Ne olursa olsun: Özellikle akışları sordunuz, ancak iç içe "toplama" çağrılarından kaçınmanızı şiddetle tavsiye ederim. Bunu daha sonra sürdürmek zorunda olan insanlar (ve bu gelecekte siz olabilirsiniz !) Orada ne yaptığınızı merak edecektir. En azından uygun şekilde adlandırılmış bazı yardımcı yöntemleri tanıtın.
Marco13

Yanıtlar:


4

catchİstisna konusunda açık bir konuya ne dersiniz :

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

Tamam iyi geliyor ... ama yakalamak (nfe) Ben anahtarın (Entry :: getKey) ve yanlış dize belirli bir değer almak istiyorum, bu yüzden tam olarak nerede yanlış giderse giriş yapabilirsiniz. Mümkün mü ?
AntonBoarf

@AntonBoarf Dize ayrıştırılamayan anahtarı günlüğe kaydetmek istiyorsanız, şunu kullanınnfe.getMessage()
Naman

1
@AntonBoarf kural dışı durum iletisinde hatalı biçimlendirilmiş giriş dizesi bulunur. Sorumlu anahtarı almak için, sadece istisna olduğunda açık bir arama yaparım, örneğininput.entrySet().stream() .filter(e -> e.getValue().stream().anyMatch(s -> !new Scanner(s).hasNextLong())) .map(Map.Entry::getKey) .findAny()
Holger

@Kulp. Teşekkürler ... Bu karmaşık görünüyor ... Döngü Java5 için standart kullanmanın benim durumumda daha iyi olmadığını merak ediyorum
AntonBoarf

@AntonBoarf sadece her ikisini de uygulayın ve karşılaştırın…
Holger

3

Şahsen Optionalsayı ayrıştırma etrafında bir girdi sağlamak istiyorum :

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

Ardından, kendi kodunuzu kullanarak (ve hatalı girdiyi yoksayarak ):

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

Ayrıca, bunu daha özlü yapmak için yardımcı bir yöntem düşünün:

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

Ardından sonuçları akışınızın toplayıcısında filtreleyebilirsiniz:

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

Ayrıca boş Optionalnesneleri listelerinizde tutabilirsiniz ve ardından yeni List<Optional<Long>>(yerine List<Long>) dizinlerini orijinaliyle karşılaştırarak List<String>, hatalı girdilere neden olan dizeyi bulabilirsiniz. Ayrıca, bu hatalarıMyClass#parseLong

Bununla birlikte, arzunuz hiç kötü bir girdi üzerinde işlememekse, yakalamaya çalıştığınız tüm akışı çevrelemek (Naman'ın cevabı başına) benim alacağım yol.


2

StringBuilderİstisna ile bir for anahtarı oluşturabilir ve eleaşağıdaki gibi sayısal olup olmadığını kontrol edebilirsiniz ,

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

Umarım yardımcı olur.


0

Dizede sayısal olup olmadığını kontrol edebilecek ve bunları akıştan filtreleyebilecek ve ayrıca null değerleri daha sonra toplayabilecek bir yardımcı yöntem yazabilirsiniz.

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

Bu her şeyi halleder.

Ve bunu akışınızda kullanın.

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());
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.