2015-05-27 30 views
21

tôi tự hỏi là có một thay thế choNhận n phần tử cuối cùng từ dòng

List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size()); 

với stream sử dụng?

+1

Tôi không nghĩ rằng đây là thường có thể với suối, như kích thước của một dòng suối có thể không được biết đến là một ưu tiên, hoặc thậm chí nó có thể là vô hạn. Và nếu bạn tạo luồng từ danh sách, chỉ cần sử dụng danh sách phụ, như bạn đã làm. –

+1

@tobias_k OP dường như có một danh sách hữu hạn tuy nhiên ... – Puce

+1

Nếu bạn đã có một danh sách, sau đó 'subList' là con đường để đi. Sau đó, bạn có thể sao chép, truyền trực tuyến, bất kỳ điều gì bạn muốn. –

Trả lời

19

Bạn có thể viết một nhà sưu tập tùy chỉnh như thế này:

public static <T> Collector<T, ?, List<T>> lastN(int n) { 
    return Collector.<T, Deque<T>, List<T>>of(ArrayDeque::new, (acc, t) -> { 
     if(acc.size() == n) 
      acc.pollFirst(); 
     acc.add(t); 
    }, (acc1, acc2) -> { 
     while(acc2.size() < n && !acc1.isEmpty()) { 
      acc2.addFirst(acc1.pollLast()); 
     } 
     return acc2; 
    }, ArrayList<T>::new); 
} 

Và sử dụng nó như thế này:

List<String> lastTen = input.stream().collect(lastN(10)); 
+2

Bạn không cần phải viết 'ArrayList :: new', chỉ' ArrayList :: new' là đủ vì tham chiếu phương thức luôn sử dụng kiểu suy luận chứ không phải kiểu thô (như nếu "toán tử kim cương" luôn hiện diện khi sử dụng ' :: mới'). Bạn đã sử dụng nó với 'ArrayDeque :: new'. Btw. Tôi muốn 'removeFirst' /' removeLast' trên 'pollFirst' /' pollLast' ở đây… – Holger

+2

@Holger, trước tiên tôi viết 'ArrayList :: new', nhưng Eclipse đã hiển thị cảnh báo về hàm tạo không được kiểm tra. Vâng, có lẽ đó là một vấn đề ECJ cụ thể. –

+0

Điều thú vị là, 'ArrayDeque :: new' sẽ được hưởng lợi từ một nhân chứng kiểu, khi sử dụng' ArrayDeque :: new' sẽ làm cho loại chứng kiến ​​tại cuộc gọi 'Collector.of' lỗi thời (và' 'đơn giản hơn' , Danh sách > ') trong khi loại nhân chứng tại' ArrayList :: new' là không cần thiết vì loại của nó có thể được suy ra từ loại mục tiêu. – Holger

12

Sử dụng Stream.skip()

Trả về một dòng gồm các yếu tố còn lại của dòng này sau khi loại bỏ các yếu tố n đầu tiên của dòng. Nếu luồng này chứa ít hơn n phần tử thì luồng trống sẽ được trả lại.

all.stream().skip(Math.max(0, all.size() - n)).forEach(doSomething); 
+0

Điều này không trả lại các phần tử n cuối cùng. Nó bỏ qua n đầu tiên. không phải những gì OP yêu cầu. – Bohemian

+0

@Bohemian 'skip (all.size() - n)' cung cấp cho bạn các phần tử n cuối cùng ... – Puce

+0

@puce yeah OK. Tôi sẽ chỉnh sửa điều đó trong. – Bohemian

3

Trong trường hợp dòng có kích thước không rõ, có lẽ không có cách nào xung quanh tiêu thụ toàn bộ dòng và đệm n yếu tố cuối cùng gặp cho đến nay. Bạn có thể làm điều này bằng cách sử dụng một số loại deque, hoặc một vòng đệm chuyên dụng tự động duy trì kích thước tối đa của nó (xem this related question cho một số triển khai).

public static <T> List<T> lastN(Stream<T> stream, int n) { 
    Deque<T> result = new ArrayDeque<>(n); 
    stream.forEachOrdered(x -> { 
     if (result.size() == n) { 
      result.pop(); 
     } 
     result.add(x); 
    }); 
    return new ArrayList<>(result); 
} 

Tất cả những hoạt động (size, pop, add) nên có độ phức tạp của O (1), vì vậy mức độ phức tạp tổng thể cho một dòng với chiều dài (không rõ) n sẽ O (n).

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