In einer früheren Frage [ Wie wird in Java 8 dynamisch gefiltert?] Stuart Marks gab eine wundervolle Antwort und stellte mehrere nützliche Dienstprogramme zur Verfügung, um die Auswahl von topN und topPercent aus dem Stream zu handhaben.
Ich werde sie hier aus seiner ursprünglichen Antwort aufnehmen:
@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));
};
}
Meine Fragen hier sind:
[1] So erhalten Sie Top-Elemente von 3 bis 7 aus einem Stream mit einer bestimmten Anzahl von Elementen. Wenn der Stream also Elemente von A1, A2 .. A10 enthält, rufen Sie an
topNFromRange(Comparator<Widget> cmp, long from, long to) = topNFromRange(comparing(Widget::length), 3L, 7L)
wird {A3, A4, A5, A6, A7} zurückgeben}
Der einfachste Weg, den ich mir vorstellen kann, ist, die Top 7 [T7] vom Original zu bekommen, die Top 3 [T3] vom Original zu bekommen und dann T7 - T3 zu bekommen.
[2] So erhalten Sie Top-Elemente von Top 10% bis Top 30% aus einem Stream mit einer bestimmten Anzahl von Elementen. Wenn der Stream also Elemente von X1, X2 .. X100 enthält, rufen Sie an
topPercentFromRange(Comparator<Widget> cmp, double from, double to) = topNFromRange(comparing(Widget::length), 0.10, 0.30)
gibt {X10, X11, X12, ..., X29, X30} zurück
Der einfachste Weg, den ich mir vorstellen kann, ist, die besten 30% [TP30] vom Original zu erhalten, die besten 10% [TP10] vom Original zu erhalten und dann TP30 - TP10 zu erhalten.
Welche besseren Möglichkeiten gibt es, Java 8 Lambda zu verwenden, um die oben genannten Situationen präzise auszudrücken?
Um einen Bereich von a zu erhalten
Stream<T>
, können Sieskip(long n)
zunächst eine festgelegte Anzahl von Elementen überspringen und dann aufrufen,limit(long n)
um nur eine bestimmte Anzahl von Elementen zu übernehmen.Stellen Sie sich einen Stream mit 10 Elementen vor. Um die Elemente 3 bis 7 zu erhalten, rufen Sie normalerweise Folgendes auf
List
:list.subList(3, 7);
Jetzt mit a
Stream
müssen Sie zuerst 3 Elemente überspringen und dann 7 - 3 = 4 Elemente nehmen, damit es wird:stream.skip(3).limit(4);
Als Variante der Lösung von @StuartMarks zur zweiten Antwort biete ich Ihnen die folgende Lösung an, bei der die Möglichkeit einer Verkettung bestehen bleibt. Sie funktioniert ähnlich wie bei @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))) ); }
und
IntStream.range(0, 100) .boxed() .collect(topPercentFromRangeCollector(Comparator.comparingInt(i -> i), 0.1d, 0.3d)) .forEach(System.out::println);
Dadurch werden die Elemente 10 bis 29 gedruckt.
Es funktioniert mit a
Collector<T, ?, Stream<T>>
, das Ihre Elemente aus dem Stream aufnimmt, sie in a umwandeltList<T>
, dann a erhältStream<T>
, es sortiert und die (richtigen) Grenzen darauf anwendet.quelle