问题
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