簡體   English   中英

在Java中使用相同的列表和流兩次

[英]Using the same list with streams twice in Java

我必須用流完成這個簡單的操作:給出一個列表,得到總和和前20個元素的總和。

這就是我的想法

IntStream stream = obj.stream().mapToInt(d->d.getInt());
stream.limit(20).sum() / stream.sum();

但是我不能這樣做,因為我被告知我不能重復使用流,所以..我嘗試了以下內容:

List<Integer> counts = obj.stream()
    .mapToInt(d -> d.getInt())
    .boxed().collect(Collectors.toList());

counts.stream().limit(20).sum() / counts.stream().sum();

但是我被告知我不能在Stream上使用sum,所以我需要再次mapToInt用於這個簡單操作的左右兩側。

有沒有辦法使用流以更優雅和簡潔的方式執行此操作?

在單個並行傳遞中有一種很酷的方法,但不使用流。

int[] nums = obj.stream().mapToInt(Integer::intValue).toArray();
Arrays.parallelPrefix(nums, Integer::sum);
int sumOfFirst20 = nums[19];
int sumOfAll = nums[nums.length-1];

parallelPrefix方法將在適當的位置計算數組上指定函數(此處為plus)的部分和的序列。 所以第一個元素是a0; 第二個是a0 + a1,第三個是a0 + a1 + a2等。

您可以將它們轉換為帶有toArray()int[] ,而不是在List<Integer>中收集數字。 這樣,代碼更緊湊,您不必一直打包和取消打包,並且可以將int[]直接轉換為IntStream

int[] nums = obj.stream().mapToInt(d -> d.getInt()).toArray();
IntStream.of(nums).limit(20).sum() / IntStream.of(nums).sum();

沒有必要使事情過於復雜。 只需從列表中獲取兩次流:

int totalSum = obj.stream().mapToInt(i -> i).sum();
int first20 = obj.stream().limit(20).mapToInt(i -> i).sum();

是的,它會在列表中進行兩次傳遞(第二次僅用於20個第一個元素,所以沒什么大不了的),所以我希望這是他們想要你做的。 它簡單,高效且易讀。


要在一次通過中執行此操作,您可以使用收集器。 舉個例子,你可以這樣做:

 Map<Boolean, Integer> map = IntStream.range(0, obj.size())
                                      .boxed()
                                      .collect(partitioningBy(i -> i < 20, 
                                               mapping(i -> obj.get(i), 
                                                       summingInt(i -> i))));

int first20 = map.get(true);
int totalSum = map.get(true) + map.get(false);

基本上,您首先流式傳輸索引。 然后為每個指標; 你將它們分成兩個列表中的地圖,一個用於20個第一個索引,另一個用於另一個列表; 產生Map<Boolean, List<Integer>>

然后將每個索引映射到原始List值(使用mapping收集器)。

最后,通過將每個List<Integer>的值合並為一個Integer轉換每個List<Integer> 然后你得到總和。

自定義收集器的另一種解決方法:

public static Collector<Integer, Integer[], Integer[]> limitSum(int limit, List<Integer> list) {
    return Collector.of(() -> new Integer[]{0, 0},
        (a, t) -> { a[0] += list.get(t); if(t < limit) a[1] += list.get(t); },
        (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
        a -> a);
}

以及用法示例:

List<Integer> obj = Stream.iterate(0, x -> x + 1).limit(5).collect(toList()); //[0, 1, 2, 3, 4]
Integer[] result = IntStream.range(0, obj.size())
                            .boxed()
                            .collect(limitSum(4, obj));
System.out.println(Arrays.toString(result)); //[10, 6]


正如你所看到的那樣,它的可讀性並不高,正如評論中所提到的,在這種情況下,for循環可能是合適的(盡管你被要求使用Streams)。

暫無
暫無

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

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