Getting two different outputs from a Stream

我只是一个虾纸丫 提交于 2019-12-10 13:49:17

问题


I am testing out the new Stream API in java-8 and want to check the outcome of 10000 random 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?


回答1:


This first solution is relying on the fact that counting the number of heads and tails of 10 000 coinflips follows a binomial law.

For this particular use case, you can use the summaryStatistics method.

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.
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}



回答2:


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> 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. 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>.




回答3:


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


来源:https://stackoverflow.com/questions/23699178/getting-two-different-outputs-from-a-stream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!