简体   繁体   English

从Stream获取两个不同的输出

[英]Getting two different outputs from a Stream

I am testing out the new Stream API in java-8 and want to check the outcome of 10000 random coinflips. 我正在测试java-8中的新Stream API ,并想检查10000随机coinflips的结果。 So far I have: 到目前为止,我有:

    public static void main(String[] args) {

        Random r = new Random();
        IntStream randomStream = r.ints(10000,0, 2);

        System.out.println("Heads: " + randomStream.filter(x -> x==1).count());
        System.out.println("Tails: " + randomStream.filter(x -> x==0).count());
    }

but this throws the exception: 但这引发了异常:

 java.lang.IllegalStateException: stream has already been operated upon or closed

I understand why this is happenning but how can i print the count for heads and tails if I can only use the stream once? 我理解为什么会发生这种情况,但如果我只能使用一次流,我该如何打印头尾的计数呢?

This first solution is relying on the fact that counting the number of heads and tails of 10 000 coinflips follows a binomial law. 第一种解决方案依赖于这样一个事实,即按照二项式法则计算10 000个翻转头的尾部和尾部的数量。

For this particular use case, you can use the summaryStatistics method. 对于此特定用例,您可以使用summaryStatistics方法。

Random r = new Random();
IntStream randomStream = r.ints(10000,0, 2);
IntSummaryStatistics stats =  randomStream.summaryStatistics();
System.out.println("Heads: "+ stats.getSum());
System.out.println("Tails: "+(stats.getCount()-stats.getSum()));


Otherwise you can use the collect operation to create a map which will map each possible result with its number of occurences in the stream. 否则,您可以使用collect操作创建一个映射,该映射将映射每个可能的结果及其在流中的出现次数。

 Map<Integer, Integer> map = randomStream .collect(HashMap::new, (m, key) -> m.merge(key, 1, Integer::sum), Map::putAll); System.out.println(map); //{0=4976, 1=5024} 

The advantage of the last solution is that this works for any bounds you give for the random integers you want to generate. 最后一个解决方案的优点是,这适用于您为要生成的随机整数提供的任何边界。

Example: 例:

 IntStream randomStream = r.ints(10000,0, 5); .... map => {0=1991, 1=1961, 2=2048, 3=1985, 4=2015} 

While all other answers are correct, they are formulated a bit cumbersome. 虽然所有其他答案都是正确的,但它们的制定有点麻烦。

Map<Integer, Long> , maps the flipped coin to a count. Map<Integer, Long> ,将翻转的硬币映射到计数。

Map<Integer, Long> coinCount = new Random().ints(10000, 0, 2)
        .boxed()
        .collect(Collectors.groupingBy(i -> i, Collectors.counting()));

This will first create the IntStream , then box them to an Stream<Integer> , as you will be storing them in their boxed version anyhow in this example. 这将首先创建IntStream ,然后将它们IntStream Stream<Integer> ,因为在本例中无论如何都要将它们存储在盒装版本中。 And lastly collect them with a groupingBy function on the identity i -> i , which gives you a Map<Integer, List<Integer>> , which is not what you want, hence you replace the List<Integer> with the operation Collectors.counting() on it, such that the List<Integer> becomes a Long , hence resulting in a Map<Integer, Long> . 最后用身份i -> i上的groupingBy函数收集它们,它给你一个Map<Integer, List<Integer>> ,这不是你想要的,因此你用操作Collectors.counting()替换List<Integer> Collectors.counting()对它进行Collectors.counting() ,使得List<Integer>变为Long ,从而产生Map<Integer, Long>

You can collect several results in a single iteration, if you want to get two outputs. 如果要获得两个输出,可以在一次迭代中收集多个结果。 In your case, it might look as follows: 在您的情况下,它可能看起来如下:

Random r = new Random();
IntStream randomStream = r.ints(10000,0, 2);

int[] counts = randomStream.collect(
    () -> new int[] { 0, 0 }, // supplier
    (a, v) -> a[v]++, // accumulator
    (l, r) -> { l[0] += r[0]; l[1] += r[1]; }); // combiner

System.out.println("Heads: " + counts[0]);
System.out.println("Tails: " + counts[1]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM