2015-05-22 28 views
6
stream.map(obj -> doMap(obj)).collect(Collectors.toList()); 

private String doMap(Object obj) { 
    if (objectIsInvalid) { 
    throw new ParseException("Object could not be parsed"); 
    } 
} 

Vấn đề: làm thế nào tôi có thể ném ngoại lệ và làm cho luồng lặp lại biết rằng nó không nên phá vỡ toàn bộ lặp, nhưng tiếp tục với phần tử tiếp theo (và cuối cùng đăng nhập các đối tượng không thành công)?Làm cách nào để lặp lại luồng ngay cả khi ngoại lệ được ném?

+2

Nếu bạn không muốn trả lại bất cứ điều gì khi một ngoại lệ được ném, có lẽ bạn nên sử dụng 'flatmap' thay vì' map'. – Balduz

+1

Có lẽ bạn chỉ có thể trả về null thay vì một String và lọc trên các đối tượng không null? – Davio

+0

@membersound, là 'ParseException' một cái gì đó bạn tự tạo ra, hoặc' java.text.ParseException'? – aioobe

Trả lời

2

Nếu không có một ngoại lệ, bạn có thể làm việc với Optionals:

stream.map(obj -> doMap(obj)) 
     .filter(obj -> obj.isPresent()) 
     .collect(Collectors.toList()); 

private Optional<String> doMap(Object obj) { 
    if (objectIsInvalid) { 
    return Optional.empty(); 
    } 
} 
+1

Tôi đoán nó phụ thuộc vào những gì bạn đang làm, nhưng âm thầm ức chế ngoại lệ thường là một ý tưởng rất xấu. Nếu ai đó chuyển vào dữ liệu không hợp lệ hoặc nếu người dùng đã chỉ định đầu vào không hợp lệ, bạn nên nói cho họ biết về nó, không hoạt động như thể mọi thứ đều ổn. – VGR

+3

Tôi không biết tại sao bạn muốn "làm việc với nulls * và * Optionals". Thông thường, bạn nên quyết định * hoặc *, bằng cách sử dụng 'null' hoặc sử dụng' Tùy chọn'. Mã của bạn trả về 'null' trong' doMap' và cố gắng gọi '.isPresent()' trên nó trong 'filter (…)'… – Holger

+2

Xin đừng làm điều này. Nếu một cái gì đó không hợp lệ, bạn có thể muốn trả về một * empty * 'Optional' thay vì' null'. Khi nó đứng, bạn sẽ nhận được NPE khi 'isPresent' được gọi trên' null'. –

2

Nếu bạn muốn có một phương tiện để lặng lẽ bỏ qua những yếu tố mà ném một ngoại lệ khi ánh xạ, bạn có thể xác định một phương pháp helper rằng hoặc trả về một thành công hoặc không có gì , dưới dạng một luồng gồm 0 hoặc 1 phần tử, và sau đó là flatMap luồng của bạn bằng phương thức đó.

import static java.util.stream.Collectors.toList; 

import java.util.List; 
import java.util.function.Supplier; 
import java.util.stream.Stream; 

public class Fish { 
    private int weight; 
    public Fish(int weight) { this.weight = weight; } 
    public String size() { 
     if (weight < 10) { 
      return "small"; 
     } else if (weight < 50) { 
      return "medium"; 
     } else if (weight < 100) { 
      return "large"; 
     } else { 
      throw new RuntimeException("I don't believe you"); 
     } 
    } 

    public static <T> Stream<T> successOrNothing(Supplier<T> supplier) { 
     try { 
     return Stream.of(supplier.get()); 
     } catch (Exception e) { 
     return Stream.empty(); 
     } 
    } 

    public static void main(String[] args) { 
     Stream<Fish> fishes = Stream.of(new Fish(1000), new Fish(2)); 
     List<String> sizes = fishes 
      .flatMap(fish -> successOrNothing(fish::size)) 
      .collect(toList()); 
     System.out.println(sizes); 
    } 
} 

Mỗi phần tử trong luồng được ánh xạ tới 1 phần tử (nếu cuộc gọi được trả về thành công) hoặc 0 (nếu cuộc gọi không thành công). Điều này có thể dễ dàng hơn hoặc đơn giản hơn sau đó "thay đổi phương thức trả về null hoặc Optional.empty() thay vì ném" mẫu, đặc biệt khi xử lý một phương thức có hợp đồng đã được xác định và bạn không muốn thay đổi.

Lưu ý: giải pháp này vẫn tự động bỏ qua các ngoại lệ; điều đó không phù hợp trong nhiều tình huống.

3

Đây là một mẹo kỳ lạ mà bạn có thể sử dụng để cải thiện việc xử lý ngoại lệ của mình.

Hãy nói rằng chức năng ánh xạ của bạn là như thế này:

String doMap(Object obj) { 
    if (isInvalid(obj)) { 
     throw new IllegalArgumentException("info about obj"); 
    } else { 
     return obj.toString(); 
    } 
} 

này trả về kết quả là nếu đối tượng có giá trị, nhưng nó ném một ngoại lệ nếu đối tượng không hợp lệ. Thật không may nếu bạn dính trực tiếp vào một đường ống, bất kỳ lỗi nào cũng sẽ dừng việc thực thi đường ống. Những gì bạn muốn là một cái gì đó giống như một loại "hoặc" có thể giữ một giá trị hoặc một chỉ báo lỗi (mà sẽ là một ngoại lệ trong Java).

Chỉ ra rằng CompletableFuture có thể chứa giá trị hoặc ngoại lệ. Mặc dù nó được thiết kế để xử lý không đồng bộ - điều này không xảy ra ở đây - chúng tôi chỉ phải sử dụng nó một chút để sử dụng cho mục đích của chúng tôi.

Đầu tiên, cho một stream của các đối tượng để xử lý, chúng ta gọi hàm lập bản đồ được bọc trong một cuộc gọi đến supplyAsync:

CompletableFuture<String>[] cfArray = 
     stream.map(obj -> CompletableFuture.supplyAsync(() -> doMap(obj), Runnable::run)) 
       .toArray(n -> (CompletableFuture<String>[])new CompletableFuture<?>[n]); 

(Thật không may, việc tạo mảng chung đưa ra một cảnh báo không được kiểm soát, trong đó sẽ phải đàn áp.)

các cấu trúc kỳ lạ

CompletableFuture.supplyAsync(supplier, Runnable::run) 

chạy các nhà cung cấp "không đồng bộ" trên Executor cung cấp Runnable::run, mà chỉ đơn giản là chạy nhiệm vụ ngay lập tức trong chủ đề này. Nói cách khác, nó vận hành đồng bộ nhà cung cấp.

Bí quyết là trường hợp CompletableFuture được trả lại từ cuộc gọi này chứa giá trị từ nhà cung cấp, nếu nó trả về bình thường hoặc nó có ngoại lệ, nếu nhà cung cấp đã ném một. (Tôi bỏ qua việc hủy bỏ ở đây.) Sau đó chúng tôi thu thập các trường hợp CompletableFuture vào một mảng. Tại sao một mảng? Thiết lập của nó cho phần tiếp theo:

CompletableFuture.allOf(cfArray).join(); 

Điều này thường chờ cho mảng CF hoàn tất.Vì chúng đã được chạy đồng bộ, nên tất cả chúng đã hoàn tất. Điều quan trọng đối với trường hợp này là join() sẽ ném một số CompletionException nếu bất kỳ của CF nào trong mảng đã hoàn thành đặc biệt. Nếu tham gia hoàn thành bình thường, chúng ta chỉ có thể thu thập các giá trị trả lại. Nếu tham gia ném một ngoại lệ, chúng ta có thể truyền bá nó, hoặc chúng ta có thể bắt nó và xử lý các ngoại lệ được lưu trữ trong các CF trong mảng đó. Ví dụ:

try { 
    CompletableFuture.allOf(cfArray).join(); 
    // no errors 
    return Arrays.stream(cfArray) 
       .map(CompletableFuture::join) 
       .collect(toList()); 
} catch (CompletionException ce) { 
    long errcount = 
     Arrays.stream(cfArray) 
       .filter(CompletableFuture::isCompletedExceptionally) 
       .count(); 
    System.out.println("errcount = " + errcount); 
    return Collections.emptyList(); 
} 

Nếu tất cả đều thành công, điều này sẽ trả về danh sách các giá trị. Nếu có bất kỳ ngoại lệ nào, điều này sẽ tính số lượng ngoại lệ và trả về một danh sách trống. Tất nhiên, bạn có thể dễ dàng làm điều gì đó khác, như ghi lại các chi tiết về trường hợp ngoại lệ, lọc ra các trường hợp ngoại lệ và trả về một danh sách các giá trị hợp lệ vv

0

tôi sẽ làm sau ...

stream.map(doMapLenient()) 
     .filter(Objects::nonNull) 
     .collect(Collectors.toList()); 

private String doMap(Object obj) { 
    if (objectIsInvalid(obj)) { 
    throw new ParseException("Object could not be parsed"); 
    } 
    return "Streams are cool"; 
} 

private Function<Object, String> doMapLenient() { 
    return obj -> { 
    try { 
     return doMap(obj); 
    } catch (ParseExcepion ex) { 
     return null; 
    } 
} 

Ở đây bạn có thể thêm độc đáo cũng có một số đăng nhập trong phần bắt

Các vấn đề liên quan