簡體   English   中英

如何使用 Java 8 lambda 從流中獲取一系列項目?

[英]How to get a range of items from stream using Java 8 lambda?

在上一個問題 [ 如何在 Java 8 中動態進行過濾? ] Stuart Marks 給出了一個很好的答案,並提供了幾個有用的實用程序來處理從流中選擇 topN 和 topPercent。

我將從他的原始答案中將它們包括在內:

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

我的問題是:

[1] 如何從具有一定數量項目的流中獲取從 3 到 7 的頂級項目,因此如果流具有來自 A1、A2 .. A10 的項目,則調用

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

將返回 { A3, A4, A5, A6, A7 }

我能想到的最簡單的方法是從原始獲得前7個[ T7 ],從原始獲得前3個[ T3 ],然后獲得T7 - T3。

[2] 如何從具有一定數量項目的流中獲取從前 10% 到前 30% 的頂級項目,因此如果流具有來自 X1、X2 .. X100 的項目,則調用

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

將返回 { X10, X11, X12, ..., X29, X30 }

我能想到的最簡單的方法是從原始獲取前 30% [ TP30 ],從原始獲取前 10% [ TP10 ],然后獲取 TP30 - TP10。

有什么更好的方法可以使用 Java 8 Lambda 來簡潔地表達上述情況?

要從Stream<T>獲取范圍,您可以使用skip(long n)首先跳過一定數量的元素,然后您可以調用limit(long n)以僅獲取特定數量的項目。

考慮一個包含 10 個元素的流,然后要獲取 3 到 7 個元素,您通常會從List調用:

list.subList(3, 7);

現在有了Stream ,你需要先跳過 3 個項目,然后取 7 - 3 = 4 個項目,所以它變成:

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

作為@StuartMarks 對第二個答案的解決方案的一種變體,我將為您提供以下解決方案,它可以保持鏈接完整的可能性,它的工作方式類似於@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)))
    );
}

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

這將打印元素 10 到 29。

它通過使用Collector<T, ?, Stream<T>>從流中獲取元素,將它們轉換為List<T> ,然后獲取Stream<T> ,對其進行排序並應用(正確的)以它為界。

用戶skiwi 已經回答了問題的第一部分。 第二部分是:

(2) 如何從具有一定數量項目的流中獲取從前 10% 到前 30% 的頂級項目....

為此,您必須在我對另一個問題的回答中使用與topPercent類似的技術。 也就是說,您必須將元素收集到一個列表中,以便能夠獲得元素的計數,這可能是在某些上游過濾完成之后。

獲得計數后,您可以根據所需的計數和百分比計算skiplimit的正確值。 像這樣的事情可能會奏效:

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

當然,您必須對fromto進行錯誤檢查。 一個更微妙的問題是確定要發射多少個元素。 例如,如果您有十個元素,它們位於索引 [0..9] 處,對應於 0%、10%、20%、...、90%。 但是,如果您要求 9% 到 11% 的范圍,上面的代碼將根本不會發出任何元素,而不是像您期望的那樣在 10% 處發出任何元素。 因此,可能需要對百分比計算進行一些修改以適應您嘗試執行的操作的語義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM