2015-11-21 29 views
6

Tôi đang cố gắng viết một phương thức tìm các chỉ mục của một đối tượng trong danh sách các danh sách và tận dụng lợi thế của tính song song. Đây là mã của tôi.Sử dụng các luồng để tìm một đối tượng trong danh sách các danh sách

// returns [i, j] where lists.get(i).get(j) equals o, or null if o is not present. 
public static int[] indices(List<? extends List<?>> lists, Object o) { 
    return IntStream.range(0, lists.size()) 
        .boxed() 
        .flatMap(i -> IntStream.range(0, lists.get(i).size()).mapToObj(j -> new int[]{i, j})) 
        .parallel() 
        .filter(a -> { 
         System.out.println(Arrays.toString(a));  // For testing only 
         return Objects.equals(o, lists.get(a[0]).get(a[1])); 
        }) 
        .findAny() 
        .orElse(null); 
} 

Khi tôi chạy đoạn mã sau

List<List<String>> lists = Arrays.asList(
     Arrays.asList("A", "B", "C"), 
     Arrays.asList("D", "E", "F", "G"), 
     Arrays.asList("H", "I"), 
     Collections.nCopies(5, "J") 
); 
System.out.println("Indices are " + Arrays.toString(indices(lists, "J"))); 

đầu ra là một cái gì đó giống như

[0, 0] 
[0, 1] 
[0, 2] 
[3, 0] 
[3, 1] 
[3, 2] 
[3, 3] 
[2, 0] 
[3, 4] 
[1, 0] 
[1, 1] 
[2, 1] 
[1, 2] 
[1, 3] 
Indices are [3, 0] 

Nói cách khác, việc tìm kiếm vẫn tiếp tục ngay cả sau khi các đối tượng đã được tìm thấy. Không phải là findAny phải là một hoạt động ngắn mạch? Tôi đang thiếu gì? Ngoài ra, cách tốt nhất để tận dụng lợi thế của tính song song khi lặp qua danh sách các danh sách hoặc một mảng bị lởm chởm là gì?

EDIT

Tiếp nối ý tưởng trong @ câu trả lời Sotirios, tôi có một sản lượng

Thread[ForkJoinPool.commonPool-worker-3,5,main] [3, 0] 
Thread[main,5,main] [2, 0] 
Thread[main,5,main] [2, 1] 
Thread[ForkJoinPool.commonPool-worker-1,5,main] [1, 0] 
Thread[ForkJoinPool.commonPool-worker-1,5,main] [1, 1] 
Thread[ForkJoinPool.commonPool-worker-1,5,main] [1, 2] 
Thread[ForkJoinPool.commonPool-worker-1,5,main] [1, 3] 
Thread[main,5,main] [0, 0] 
Thread[main,5,main] [0, 1] 
Thread[ForkJoinPool.commonPool-worker-3,5,main] [3, 1] 
Thread[main,5,main] [0, 2] 
Thread[ForkJoinPool.commonPool-worker-3,5,main] [3, 2] 
Thread[ForkJoinPool.commonPool-worker-3,5,main] [3, 3] 
Thread[ForkJoinPool.commonPool-worker-3,5,main] [3, 4] 
Indices are [3, 0] 

ý rằng

Thread[ForkJoinPool.commonPool-worker-3,5,main] 

tiếp tục tìm kiếm ngay cả sau khi câu trả lời được tìm thấy.

+0

sử dụng findFirst() để thay thế. –

+0

@TaharBakir Nó vẫn tiếp tục tìm kiếm. –

+1

Ngoài ra, song song có thể mất một thời gian trước khi một luồng có thể thông báo cho những người khác rằng họ không cần phải tiếp tục. –

Trả lời

7

hoạt động ngắn mạch không đảm bảo chỉ kéo như vài yếu tố như nó cần để tạo ra kết quả của họ. Họ có thể làm như vậy, nhưng không bắt buộc.

Việc triển khai hiện tại là flatMap là như vậy sẽ luôn đẩy toàn bộ nội dung của dòng dưới xuôi xuống dưới. Vì vậy, ngay cả khi luồng của bạn không song song, bạn có thể thấy nhiều yếu tố chảy qua luồng hơn so với khi cần để đáp ứng findAny.

+0

Có vẻ như câu trả lời này là đúng, và bộ lọc 'flatMap().(). FindAny()' về cơ bản không phải là ngắn mạch. Tôi không biết tại sao nó sẽ được thực hiện theo cách này. –

+1

"ngắn cuircuiting" chỉ có nghĩa là nó * có thể * chấm dứt trước khi kiểm tra toàn bộ dòng. Nó không thực hiện bất kỳ sự bảo đảm nào ngoài điều đó. – Misha

1

Nó không phải là nó tiếp tục, nó là nó đã gửi tất cả các loại chủ đề để thử và tìm kết quả và sẽ chờ đợi cho đến khi những người đã hoàn thành trước khi trả lại kết quả.

Nói cách khác, thao tác thiết bị đầu cuối findAny sẽ gửi tác vụ "tìm kiếm" đến một số chuỗi. Các tác vụ này chỉ đơn giản là áp dụng filterPredicate và trả lại khi có điều gì đó trả về true. findAny, có lẽ, chờ một trong số này trả về một giá trị. Không có cách nào để nó thực sự hủy bỏ bất cứ điều gì nó đã gửi và có vẻ như việc thực hiện này sẽ chặn cho đến khi toàn bộ lô trả về. Nó chỉ có thể ngừng gửi bất kỳ lô tương lai nào.

Bạn có thể xác minh điều này bằng cách đăng nhập thread hiện tại:

System.out.println(Thread.currentThread() + " " + Arrays.toString(a)); // For testing only 
+0

Tôi đang ngủ, vì vậy đây có lẽ là một câu hỏi ngu ngốc, nhưng nếu một chuỗi các chuỗi công việc được giao nhiệm vụ phía trước, và toàn bộ phương pháp không thể quay trở lại cho đến khi tất cả kết thúc, thì mạch ngắn có nghĩa là gì? –

+1

@PaulBoddington Tôi không nghĩ đó là _all_, tôi nghĩ đó là một số tập hợp con. –

+1

@PaulBoddington Ví dụ: tôi sẽ tắt 5 chuỗi để tìm kiếm. Tất cả 5 có thể trả lại kết quả. Nhưng tôi phải đợi cả 5 trước khi tôi có thể quyết định. (Vâng, bạn thực sự chỉ phải chờ đợi một, nhưng bạn không thể hủy bỏ những người khác. Và thực hiện này dường như muốn tham gia vào tất cả những nhiệm vụ 5.) –

2

Đối với "lý do tại sao nó được triển khai theo cách này". Vấn đề nằm sâu trong việc triển khai API luồng. Cơ quan flatMap thường tạo một luồng với một số hoạt động trung gian (như .flatMap(list -> list.stream().map(...).filter(...))). Người ta có thể sử dụng bên trong việc triển khai flatMapstream.spliterator() và gọi tryAdvance nhiều lần cho đến khi yêu cầu hủy. Tuy nhiên, cuộc gọi spliterator() trả về một số trình tách biệt nhân tạo khi luồng chứa các hoạt động trung gian (nếu không, nó chỉ trả về bộ tách dòng gốc).Trình tách rời nhân tạo này không thực hiện hiệu quả việc thực hiện tryAdvance() rất hiệu quả, do đó việc sử dụng triển khai này có thể được coi là hạn chế hiệu suất tồi tệ hơn so với việc tiêu thụ toàn bộ luồng được ánh xạ. Trong nhiều trường hợp bạn flatMap đến một số dòng ngắn, vì vậy ở đây bạn có thể có được hiệu suất nhờ vào việc thực hiện hiện tại.

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