2014-04-07 11 views
21

W poprzednim pytaniu [How to dynamically do filtering in Java 8?] Stuart Marks podał wspaniałą odpowiedź i dostarczył kilka przydatnych narzędzi do obsługi wyboru topN i topPercent ze strumienia.Jak uzyskać szereg produktów ze strumienia za pomocą Java 8 lambda?

będę włączyć je tutaj ze swojego oryginalnego odpowiedź:

@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)); 
    }; 
} 

Moje pytania są tutaj:

[1] Jak uzyskać najwyższe pozycje od 3 do 7 ze strumienia z pewnej wysokości przedmiotów, tak, że strumień ma elementy z A1, A2 .. A10 wywołaniu

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

powróci {A3, A4, A5, A6, A7}

Najprostszym sposobem, jaki mogę wymyślić, jest uzyskanie pierwszej 7 [T7] z oryginału, zdobycie pierwszej 3 [T3] z oryginału, a następnie uzyskanie T7 - ​​T3.

[2] Jak uzyskać najlepsze elementy z górnej 10% do 30% od góry strumienia z pewnej ilości elementów, więc jeżeli strumień posiada elementy z X1, X2 .. X100, wezwanie do

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

powróci {X10, X11, X12, X29, ..., X30}

Najprostszym sposobem mogę myśleć, to dostać górny 30% [TP30] od oryginału, dostać Top 10% [TP10 ] z oryginału, a następnie pobierz TP30 - TP10.

Jakie są lepsze sposoby wykorzystania Javy 8 Lambdy do zwięzłego wyrażenia powyższych sytuacji?

Odpowiedz

19

Użytkownik skiwi już answered pierwsza część pytania.Druga część to:

(2) Jak uzyskać najlepsze elementy z górnej 10% do 30% od góry strumienia z pewnej ilości elementów ....

Aby to zrobić, Muszę użyć podobnej techniki, jak topPercent w moim answer do drugiego pytania. Oznacza to, że musisz zebrać elementy do listy, aby móc uzyskać liczbę elementów, prawdopodobnie po wykonaniu wcześniejszego filtrowania.

Po obliczeniu należy obliczyć odpowiednie wartości dla wartości skip i limit na podstawie liczby i żądanych procentów. Coś jak to może działać:

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))); 
    }; 
} 

Oczywiście trzeba będzie zrobić sprawdzanie błędów na from i to. Bardziej subtelnym problemem jest określenie liczby emitowanych elementów. Na przykład, jeśli masz dziesięć elementów, są one w indeksach [0..9], które odpowiadają 0%, 10%, 20%, ..., 90%. Ale gdybyś poprosił o zakres od 9% do 11%, powyższy kod nie wydzielałby żadnych elementów, a nie ten o wartości 10%, jak można by się spodziewać. Więc niektóre majsterkowanie z procentowymi obliczeniami jest prawdopodobnie konieczne, aby dopasować semantykę tego, co próbujesz zrobić.

+0

Wystarczająco blisko tego, czego szukałem, opracuję szczegóły, dzięki! – Frank

+0

Uaktualniłem moją odpowiedź, aby zawierało również formę tego, co robisz, ale potem używając Kolekcjonerów, być może mogłoby to być interesujące również dla pierwotnego pytania o kryteria? – skiwi

+0

@skiwi Ciekawe, używając funkcji finiszera kolektora, aby z powrotem przywrócić kolekcję do strumienia. Nie jestem pewien, czy jest to lepsze, czy gorsze od deklarowania zmiennej lokalnej. (W tym przypadku parametr lambda jest używany jako lokalny). Jest to jednak przydatna technika, o której należy pamiętać w przyszłości. –

21

Aby uzyskać zakres od Stream<T>, można użyć skip(long n), aby najpierw pominąć określoną liczbę elementów, a następnie można zadzwonić pod numer limit(long n), aby pobrać tylko określoną liczbę elementów.

Rozważmy strumień z 10 elementów, a następnie dostać się elementy 3 do 7, normalnie można dzwonić z List:

list.subList(3, 7); 

teraz z Stream, trzeba najpierw przejść 3 pozycji, a następnie podjąć 7 - 3 = 4 pozycji, więc staje się:

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

w wariancie rozwiązania @StuartMarks' do drugiej odpowiedzi, będę oferują następujące rozwiązanie, które przewiduje możliwość łańcucha nienaruszonym, to działa podobnie jak to robi @StuartMarks:

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))) 
    ); 
} 

i

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

To drukować elementów 10 przez 29.

Działa przy użyciu Collector<T, ?, Stream<T>> które ma w swoich elementów ze strumienia, przekształca je w List<T>, a następnie otrzymuje Stream<T>, sortuje i stosuje (poprawne) granice do niego.

+0

Jeśli pominiesz pierwsze 10% przedmiotów, to w strumieniu pozostało tylko 90% przedmiotów, jak zdobyć przedmioty z oryginalnego 30%, ponieważ 30% z 90% nie jest oryginałem 30% , mam rację ? – Frank

+1

@Frank Należy obliczyć te liczby z góry. – skiwi

+0

Dobra, dzięki! – Frank

Powiązane problemy