2014-04-07 35 views
21

Trong câu hỏi trước [How to dynamically do filtering in Java 8?] Stuart Marks đã đưa ra một câu trả lời tuyệt vời và cung cấp một số tiện ích hữu ích để xử lý lựa chọn topN và topPercent từ luồng.Làm thế nào để có được một loạt các mục từ luồng bằng cách sử dụng Java 8 lambda?

tôi sẽ bao gồm chúng ở đây từ câu trả lời ban đầu của mình:

@FunctionalInterface 
public interface Criterion { 
    Stream<Widget> apply(Stream<Widget> s); 
} 

Criterion topN(Comparator<Widget> cmp, long n) { 
    return stream -> stream.sorted(cmp).limit(n); 
} 

Criterion topPercent(Comparator<Widget> cmp, double pct) { 
    return stream -> { 
     List<Widget> temp = 
      stream.sorted(cmp).collect(toList()); 
     return temp.stream() 
        .limit((long)(temp.size() * pct)); 
    }; 
} 

Câu hỏi của tôi ở đây là:

[1] Làm thế nào để có được mục top 3-7 từ một dòng với số tiền nhất định của mặt hàng, vì vậy nếu dòng có mặt hàng từ A1, A2 .. A10, cuộc gọi đến

topNFromRange(Comparator<Widget> cmp, long from, long to) = topNFromRange(comparing(Widget::length), 3L, 7L) 

sẽ trở lại {A3, A4, A5, A6, A7}

Cách đơn giản nhất tôi có thể nghĩ đến là nhận được từ 7 [T7] hàng đầu từ bản gốc, nhận được từ 3 [T3] hàng đầu từ bản gốc và sau đó nhận T7 - ​​T3.

[2] Làm thế nào để có được mặt hàng đầu từ trên 10% đến 30% đầu từ một dòng với số tiền nhất định của mặt hàng, vì vậy nếu dòng có mặt hàng từ X1, X2 .. X100, cuộc gọi đến

topPercentFromRange(Comparator<Widget> cmp, double from, double to) = topNFromRange(comparing(Widget::length), 0.10, 0.30) 

sẽ trở lại {X10, X11, X12, ..., X29, X30}

cách đơn giản nhất tôi có thể nghĩ đến là có được top 30% [TP30] từ gốc, có được top 10% [TP10 ] từ bản gốc, và sau đó nhận TP30 - TP10.

Một số cách tốt hơn để sử dụng Java 8 Lambda để thể hiện chính xác các tình huống trên là gì?

Trả lời

19

Skiwi người dùng đã là answered phần đầu tiên của câu hỏi.Phần thứ hai là:

(2) Làm thế nào để có được mặt hàng đầu từ trên 10% đến 30% đầu từ một dòng với số tiền nhất định của các mặt hàng ....

Để làm điều này, bạn phải sử dụng kỹ thuật tương tự như topPercent trong câu hỏi answer của tôi. Tức là, bạn phải thu thập các phần tử vào một danh sách để có thể đếm được các phần tử, có thể sau khi một số lọc ngược dòng đã được thực hiện.

Khi bạn có số, sau đó bạn tính giá trị phù hợp cho skiplimit dựa trên số lượng và phần trăm bạn muốn. Một cái gì đó như thế này có thể làm việc:

Criterion topPercentFromRange(Comparator<Widget> cmp, double from, double to) { 
    return stream -> { 
     List<Widget> temp = 
      stream.sorted(cmp).collect(toList()); 
     return temp.stream() 
        .skip((long)(temp.size() * from)) 
        .limit((long)(temp.size() * (to - from))); 
    }; 
} 

Tất nhiên bạn sẽ phải làm kiểm tra lỗi trên fromto. Một vấn đề tinh tế hơn là xác định có bao nhiêu yếu tố phát ra. Ví dụ: nếu bạn có mười phần tử, chúng có chỉ mục [0..9], tương ứng với 0%, 10%, 20%, ..., 90%. Nhưng nếu bạn yêu cầu phạm vi từ 9% đến 11%, mã ở trên sẽ không phát ra bất kỳ yếu tố nào, không phải là yếu tố ở mức 10% như bạn mong đợi. Vì vậy, một số tinkering với các tính toán tỷ lệ phần trăm có lẽ là cần thiết để phù hợp với ngữ nghĩa của những gì bạn đang cố gắng làm.

+0

Đóng đủ để những gì tôi đang tìm kiếm, tôi sẽ làm việc ra các chi tiết, cảm ơn! – Frank

+0

Tôi đã cập nhật câu trả lời của mình cũng bao gồm một hình thức của những gì bạn đang làm, nhưng sau đó với việc sử dụng Bộ sưu tập, có lẽ nó cũng có thể là thú vị cho câu hỏi ban đầu của tiêu chí? – skiwi

+0

@skiwi Thú vị, sử dụng chức năng kết thúc của một bộ sưu tập để biến bộ sưu tập trở lại thành một luồng. Tôi không chắc liệu nó có nhất thiết tốt hơn hay tệ hơn là chỉ khai báo một biến cục bộ. (Tham số lambda được sử dụng như một địa phương trong trường hợp này.) Đó là một kỹ thuật hữu ích để ghi nhớ cho tương lai, mặc dù. –

21

Để nhận phạm vi từ Stream<T>, bạn có thể sử dụng skip(long n) để bỏ qua một số phần tử đã định trước và sau đó bạn có thể gọi limit(long n) để chỉ lấy một số lượng mặt hàng cụ thể.

Hãy xem xét một dòng với 10 yếu tố, sau đó để có được các yếu tố 3-7, bạn thường gọi từ một List:

list.subList(3, 7); 

Bây giờ với một Stream, bạn cần phải đầu tiên bỏ qua 3 mặt hàng, và sau đó đi 7-3 = 4 mặt hàng, vì vậy nó trở thành:

stream.skip(3).limit(4); 

là một biến thể với giải pháp @StuartMarks' để câu trả lời thứ hai, tôi sẽ cung cấp cho bạn các giải pháp sau đó rời khỏi khả năng chuỗi nguyên vẹn, nó hoạt động tương tự để làm thế nào @StuartMarks hiện nó:

private <T> Collector<T, ?, Stream<T>> topPercentFromRangeCollector(Comparator<T> comparator, double from, double to) { 
    return Collectors.collectingAndThen(
     Collectors.toList(), 
     list -> list.stream() 
      .sorted(comparator) 
      .skip((long)(list.size() * from)) 
      .limit((long)(list.size() * (to - from))) 
    ); 
} 

IntStream.range(0, 100) 
     .boxed() 
     .collect(topPercentFromRangeCollector(Comparator.comparingInt(i -> i), 0.1d, 0.3d)) 
     .forEach(System.out::println); 

này sẽ in các yếu tố 10 đến 29.

Nó hoạt động bằng cách sử dụng một Collector<T, ?, Stream<T>> mà mất trong các yếu tố mình khỏi luồng, biến chúng thành một List<T>, sau đó có được một số Stream<T>, sắp xếp nó và áp dụng giới hạn (chính xác) cho nó.

+0

Nếu bạn bỏ qua 10% mục đầu tiên, thì chỉ còn 90% mục trong luồng, cách lấy các mục từ 30% gốc, vì 30% từ 90% không phải là 30% gốc Tôi có đúng không? – Frank

+1

@Frank Bạn sẽ cần phải tính toán những con số đó trước. – skiwi

+0

Được rồi, cảm ơn! – Frank

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