2014-10-16 15 views
7

Nếu tôi có số Stream<T>, tôi có thể dễ dàng sử dụng skip(long) để bỏ qua một vài thành phần đầu tiên của luồng. Tuy nhiên, dường như không có tương đương với việc bỏ qua một số phần tử đã cho ở cuối luồng.Bỏ qua phần tử x cuối cùng trong Dòng <T>

Giải pháp rõ ràng nhất là sử dụng limit(originalLength - elementsToRemoveAtEnd), nhưng điều đó đòi hỏi phải biết trước về độ dài ban đầu, không phải lúc nào cũng như vậy.

Có cách nào để xóa vài phần tử cuối cùng của luồng có độ dài không xác định mà không phải thu thập nó vào một số Collection, đếm các phần tử và phát lại nó không?

+4

Lý do không có phương pháp nào có thể là phương pháp bạn đang đưa ra. Thường thì không có cách nào để biết khi nào một 'Stream' sẽ kết thúc trong quá trình phát trực tuyến, vì vậy bạn không thể thực sự loại bỏ các phần tử cuối cùng cho đến khi bạn hoàn thành. – Keppil

+0

Làm thế nào để bạn xác định điểm khi bạn muốn bỏ qua các yếu tố còn lại? Bạn không thể triển khai Luồng của riêng mình để bao quanh Luồng gốc? – user

+0

Làm thế nào để bạn biết một yếu tố là cuối cùng nếu bạn không biết độ dài của dòng? – assylias

Trả lời

8

Không có giải pháp lưu trữ chung miễn phí cho Stream s có thể có độ dài không xác định. Tuy nhiên, bạn không cần phải thu thập toàn bộ dòng, bạn chỉ cần một lưu trữ lớn như số lượng các yếu tố mà bạn muốn bỏ qua:

static <T> Stream<T> skipLastElements(Stream<T> s, int count) { 
    if(count<=0) { 
     if(count==0) return s; 
     throw new IllegalArgumentException(count+" < 0"); 
    } 
    ArrayDeque<T> pending=new ArrayDeque<T>(count+1); 
    Spliterator<T> src=s.spliterator(); 
    return StreamSupport.stream(new Spliterator<T>() { 
     public boolean tryAdvance(Consumer<? super T> action) { 
      while(pending.size()<=count && src.tryAdvance(pending::add)); 
      if(pending.size()>count) { 
       action.accept(pending.remove()); 
       return true; 
      } 
      return false; 
     } 
     public Spliterator<T> trySplit() { 
      return null; 
     } 
     public long estimateSize() { 
      return src.estimateSize()-count; 
     } 
     public int characteristics() { 
      return src.characteristics(); 
     } 
    }, false); 
} 
public static void main(String[] args) { 
    skipLastElements(Stream.of("foo", "bar", "baz", "hello", "world"), 2) 
    .forEach(System.out::println); 
} 
+0

Ở trên có một vấn đề nghiêm trọng ẩn! Việc gọi 'Stream.spliterator()' có thể trả về một 'StreamSpliterators.WrappingSpliterator'. Phương thức 'initPartialTraversalState()' lưu tất cả các phần tử luồng vào một 'Bộ sưu tập' (tức là' SpinedBuffer')! Điều này có thể làm nổ tung luồng cho các luồng dựa trên các đầu vào của tệp. – Nathan

0

Các mã sau sử dụng ArrayDeque để đệm n yếu tố nơi n là số lượng các phần tử để bỏ qua ở cuối. Bí quyết là sử dụng skip(n). Điều này làm cho các thành phần n đầu tiên được thêm vào ArrayDeque. Sau đó, khi các phần tử n đã được lưu vào bộ đệm, luồng sẽ tiếp tục xử lý các phần tử nhưng bật các phần tử từ ArrayDeque. Khi kết thúc luồng, phần tử n cuối cùng bị kẹt trong ArrayDeque và bị hủy.

ArrayDeque không cho phép null yếu tố. Mã bên dưới bản đồ null thành NULL_VALUE trước khi thêm vào ArrayDeque và sau đó bản đồ NULL_VALUE quay lại null sau khi popping từ ArrayDeque.

private static final Object NULL_VALUE = new Object(); 

public static <T> Stream<T> skipLast(Stream<T> input, int n)     
{ 
    ArrayDeque<T> queue; 

    if (n <= 0) 
     return(input); 

    queue = new ArrayDeque<>(n + 1); 

    input = input. 
     map(item -> item != null ? item : NULL_VALUE). 
     peek(queue::add). 
     skip(n). 
     map(item -> queue.pop()). 
     map(item -> item != NULL_VALUE ? item : null); 

    return(input); 
} 
+1

Vâng, tôi thích giải pháp có thể không hiệu quả, tùy thuộc vào cách Luồng được triển khai, qua giải pháp có thể bị hỏng, tùy thuộc vào cách Luồng được triển khai. Giải pháp của bạn dựa trên việc triển khai hiện tại không có khả năng kết hợp 'bỏ qua' với nguồn luồng. Một phiên bản tương lai có thể thực sự bỏ qua tất cả các bước trước mà không thay đổi kích cỡ luồng, giống như 'số đếm()' của Java 9 có thể không đánh giá các chức năng này… – Holger

+0

Tôi đã nghe những tin đồn đó, nhưng những tối ưu hóa đó sẽ bị hạn chế phạm vi áp dụng hoặc nhiều mã sẽ bị hỏng. 'peek()' nên ngừng tối ưu hóa như vậy bởi vì lambda của nó hầu như luôn luôn có tác dụng phụ. Vì vậy, 'skip()' và 'count()' không thể tối ưu hóa nó đi. – Nathan

+0

'Số đếm() '* của Java 9 đã làm vỡ mã như vậy. Như đã thảo luận trong [Trong Java suối là peek thực sự chỉ để gỡ lỗi?] (Https://stackoverflow.com/q/33635717/2711488), bạn không nên sử dụng một phương pháp trái với mục đích của nó. Vì mục đích chính của phương pháp này là gỡ lỗi, tức là phát hiện * liệu các phần tử * có được xử lý ở giai đoạn này hay không, sẽ vô lý khi buộc xử lý ở giai đoạn này * vì * của phương pháp này.Bạn không chỉ có thể sử dụng phương pháp để phát hiện sự hiện diện của việc tối ưu hóa như vậy, bạn có thể phát hiện rằng các luồng đôi khi xử lý nhiều phần tử hơn mức cần thiết, trong Java 8 * và * Java 9. – Holger

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