是否有一些仅流的方法来确定最大流元素的索引?

时间:2018-04-11 19:06:02

标签: java-8 java-stream

我有Stream<Set<Integer>> intSetStream

我可以这样做......

Set<Integer> theSetWithTheMax = intSetStream.max( (x,y)->{ return Integer.compare( x.size(), y.size() ); } ).get( );

...我抓住了Set<Integer>Integer元素数量最多的Set

太棒了。但我真正需要知道的是,Stream中的第一个Set是最大值吗?或者它是Stream中的第10个i?还是Set i?其中哪一个元素最多?

所以我的问题是:有没有办法 - 使用Stream API - 我可以确定&#34; 这是Set StreamSet Set.size( )的{​​{1}}中,Stream<Set<Integer>>来电&#34;?

我能想到的最佳解决方案是迭代intSetStream.iterator()(使用max( ))并进行手动Stream计算。但是我希望能够学会更多的{{1}} - y方式去做;如果有这样的事情。

4 个答案:

答案 0 :(得分:4)

您可以使用自定义收集器执行此操作:

int posOfMax = stream.mapToInt(Set::size)
    .collect(() -> new int[] { 0, -1, -1 },
            (a,i) -> { int pos = a[0]++; if(i>a[2]) { a[1] = pos; a[2] = i; } },
            (a1,a2) -> {
                if(a2[2] > a1[2]) { a1[1] = a1[0]+a2[1]; a1[2] = a2[2]; }
                a1[0] += a2[0];
            })[1];

这是最轻量级的解决方案。当我们使用专用类而不是数组时,它的逻辑变得更清晰:

int posOfMax = stream.mapToInt(Set::size)
    .collect(() -> new Object() { int size = 0, pos = -1, max = -1; },
            (o,i) -> { int pos = o.size++; if(i>o.max) { o.pos = pos; o.max = i; } },
            (a,b) -> {
                if(b.max > a.max) { a.pos = a.size+b.pos; a.max = b.max; }
                a.size += b.size;
            }).pos;

状态对象包含大小,它只是到目前为止遇到的元素数,最后遇到的最大值及其位置,如果当前元素大于最大值,我们将更新为大小的前一个值。这就是累加器函数(collect的第二个参数)的作用。

为了支持任意评估命令,即并行流,我们必须提供组合器函数(collect的最后一个参数)。它将两个部分评估的状态合并到第一个状态。如果第二个状态的最大值更大,我们更新第一个的最大值和位置,而我们必须将第一个状态的大小添加到第二个位置,以反映两个都是部分结果的事实。此外,我们必须将大小更新为两种大小的总和。

答案 1 :(得分:2)

执行此操作的一种方法是首先将Stream<Set<Integer>>映射到Collection<Integer>,其中每个元素的大小为每个Set<Integer>,然后您可以提取给定的最大元素数Stream<Set<Integer>>然后获取&#34;索引&#34;通过查找大小集合中最大数字的索引来设置此集合。

请考虑以下示例:

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class IntSetStreamExample {

    public static void main(String[] args) {

        final Stream<Set<Integer>> stream = Stream.of(
                new HashSet<>(Arrays.asList(1,2,3)),
                new HashSet<>(Arrays.asList(1,2)),
                new HashSet<>(Arrays.asList(1,2,3,4,5)),
                new HashSet<>(Arrays.asList(0)),
                new HashSet<>(Arrays.asList(0,1,2,3,4,5)),
                new HashSet<>()
        );

        final List<Integer> result = stream.map(Set::size).collect(Collectors.toList());

        System.out.println("List of number of elements in Stream<Set<Integer>>: " + result);

        final int max = Collections.max(result);

        System.out.println("Largest set contains " + max + " elements");

        final int index = result.indexOf(max);

        System.out.println("Index of the largest set: " + index);
    }
}

示例性输出可能如下所示:

List of number of elements in Stream<Set<Integer>>: [3, 2, 5, 1, 6, 0]
Largest set contains 6 elements
Index of the largest set: 4

答案 2 :(得分:1)

Streams方法的目的不是要知道迭代的当前元素 所以我认为你实际的方式:找到具有最大元素的Set,然后迭代集合以找到这个Set也不错。

作为替代方案,你可以先将Stream<Set<Integer>>收集到一个List(以便有办法检索索引)并使用SimpleImmutableEntry,但它似乎真的有点过分了:

Stream<Set<Integer>> intSetStream = ...;
List<Set<Integer>> list = intSetStream.collect(Collectors.toList());

SimpleImmutableEntry<Integer, Set<Integer>> entry = 
        IntStream.range(0, list.size())
                 .mapToObj(i -> new SimpleImmutableEntry<>(i, list.get(i)))
                 .max((x, y) -> {
                     return Integer.compare(x.getValue()
                                             .size(),
                                            y.getValue()
                                             .size());
                 })
                 .get();

Integer index = entry.getKey();
Set<Integer> setWithMaxNbElements = entry.getValue();

答案 3 :(得分:1)

Collector的自定义解决方案中提供的洞察力(在我彻头彻尾的无耻剽窃 the source code of IntSummaryStatistics.java之上),启发了一个习惯{{1基于我自己的解决方案; 反过来可能激励他人 ......

Collector

...使用该自定义public class IndexOfMaxCollector implements IntConsumer { private int max = Integer.MIN_VALUE; private int maxIdx = -1; private int currIdx = 0; public void accept( int value ){ if( value > max ) maxIdx = currIdx; max = Math.max( max, value ); currIdx++; } public void combine( IndexOfMaxCollector other ){ if( other.max > max ){ maxIdx = other.maxIdx + currIdx; max = other.max; } currIdx += other.currIdx; } public int getMax( ){ return this.max; } public int getIndexOfMax( ){ return this.maxIdx; } } ,我可以获取我的OQ的Collector并确定包含最多元素的intSetStream的索引,如下所示。

Set<Integer>

这个解决方案 - 诚然不是最好的&#34; 美丽&#34; - 在可重用性可理解性赌注中,可能比其他人有一点优势。