Xử lý các ngoại lệ với các luồng


10

Tôi có một Map<String,List<String>>và muốn nó biến thành Map<String,List<Long>>vì mỗi Stringtrong danh sách đại diện cho một 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()))
               );

Vấn đề chính của tôi là mỗi Stringcó thể không đại diện chính xác a Long; có thể có một số vấn đề. Long::valueOfcó thể đưa ra ngoại lệ. Nếu đây là trường hợp, tôi muốn trả về null hoặc rỗngMap<String,List<Long>>

Bởi vì tôi muốn lặp đi lặp lại sau outputbản đồ này . Nhưng tôi không thể chấp nhận bất kỳ chuyển đổi lỗi nào; thậm chí không một ai Bất kỳ ý tưởng nào về cách tôi có thể trả về một đầu ra trống trong trường hợp Chuỗi không chính xác -> Chuyển đổi dài?


Tôi đồng ý với giải pháp Naman nhưng không may trong khối bắt, tôi không lấy được khóa (Entry :: getKey) mà Chuỗi -> Chuyển đổi dài không chính xác
AntonBoarf

Thảo luận tương tự ở đây: Chuỗi dữ liệu int có khả năng xấu cần tránh ngoại lệ khi cuối cùng tôi quyết định kiểm tra trước với regex (tài liệu parseLong sử dụng cùng một quy tắc phân tích cú pháp & bạn có thể muốn trả về LongStreamnếu bạn dự định xóa emptykết quả)
AjahnCharles

Xin lỗi tôi hiểu lầm. Tôi nghĩ bạn có nghĩa là trả về một mục duy nhất là trống / null; nhưng bây giờ tôi nghĩ bạn có nghĩa là toàn bộ Bản đồ!
AjahnCharles

1
Không hoàn toàn rõ ràng điểm chính là gì - bạn muốn trả về một bản đồ trống trong trường hợp có lỗi, nhưng vẫn in "phím" nơi lỗi xuất hiện trên bảng điều khiển? Ý tôi là, thông tin về bối cảnh xuất hiện ngoại lệ thường được chuyển lên ngăn xếp cuộc gọi trong ngoại lệ. Bất kể rằng: Bạn đặc biệt yêu cầu về con suối, nhưng tôi muốn mạnh mẽ khuyên bạn nên tránh lồng cuộc gọi "thu thập". Những người phải duy trì điều đó sau này (và đây có thể là tương lai của bạn !) Sẽ tự hỏi những gì ... bạn đã làm ở đó. Ít nhất là giới thiệu một số phương pháp trợ giúp được đặt tên đúng.
Marco13

Câu trả lời:


4

Làm thế nào về một rõ ràng catchvề ngoại lệ:

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

ok nghe có vẻ tốt ... nhưng trong phần bắt (nfe) tôi muốn lấy giá trị cụ thể của khóa (Entry :: getKey) và Chuỗi không chính xác mà nó bị lỗi để tôi có thể đăng nhập chính xác khi nó bị lỗi. Có thể không?
AntonBoarf

@AntonBoarf Nếu bạn chỉ muốn đăng nhập khóa mà không thể phân tích chuỗi, hãy sử dụngnfe.getMessage()
Naman

1
@AntonBoarf thông báo của ngoại lệ sẽ chứa chuỗi đầu vào không đúng định dạng. Để có được chìa khóa có trách nhiệm, tôi muốn làm một tìm kiếm rõ ràng, chỉ khi ngoại lệ xảy ra, ví dụinput.entrySet().stream() .filter(e -> e.getValue().stream().anyMatch(s -> !new Scanner(s).hasNextLong())) .map(Map.Entry::getKey) .findAny()
Holger

@Holder. Cảm ơn ... Điều đó có vẻ phức tạp ... Tôi tự hỏi nếu sử dụng standart cho vòng lặp Java5 không tốt hơn trong trường hợp của tôi
AntonBoarf

@AntonBoarf chỉ cần thực hiện cả hai và so sánh trên
Holger

3

Cá nhân tôi muốn cung cấp một Optionalđầu vào xung quanh phân tích số:

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

Sau đó, sử dụng mã của riêng bạn (và bỏ qua đầu vào xấu):

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

Ngoài ra, hãy xem xét một phương pháp trợ giúp để làm cho điều này ngắn gọn hơ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());
}

Sau đó, bạn có thể lọc kết quả trong trình thu thập luồng của mình:

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

Bạn cũng có thể giữ các Optionalđối tượng trống trong danh sách của mình và sau đó bằng cách so sánh chỉ mục của chúng trong mới List<Optional<Long>>(thay vì List<Long>) với bản gốc List<String>, bạn có thể tìm thấy chuỗi gây ra bất kỳ đầu vào sai lầm nào. Bạn cũng có thể chỉ cần đăng nhập những thất bại này trongMyClass#parseLong

Tuy nhiên, nếu mong muốn của bạn là không hoạt động trên bất kỳ đầu vào xấu nào , thì xung quanh toàn bộ luồng trong những gì bạn đang cố gắng nắm bắt (theo câu trả lời của Naman) là con đường tôi sẽ đi.


2

Bạn có thể tạo một StringBuilderkhóa for với ngoại lệ và kiểm tra xem có phải elelà số như dưới đây không,

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

Hy vọng nó giúp.


0

Có thể bạn có thể viết một phương thức trợ giúp có thể kiểm tra số trong chuỗi và lọc chúng ra khỏi luồng và cả các giá trị null sau đó cuối cùng thu thập vào Bản đồ.

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

Điều này sẽ chăm sóc tất cả mọi thứ.

Và sử dụng điều này trong luồng của bạ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());
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.