Получение двух разных выходов из потока

Я тестирую новый Stream API в java-8 и хочу проверить результат 10000 случайных совпадений. До сих пор я:

    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());
    }

но это исключает исключение:

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

Я понимаю, почему это происходит, но как я могу напечатать счетчик для голов и хвостов, если я могу использовать поток только один раз?

Ответы

Ответ 1

Это первое решение основывается на том факте, что подсчет количества голов и хвостов в 10 000 коллиндов соответствует биномиальному закону.

В этом конкретном случае использования вы можете использовать метод 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()));

<ч/" > В противном случае вы можете использовать операцию 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}

Преимущество последнего решения заключается в том, что это работает для любых ограничений, которые вы даете для случайных целых чисел, которые вы хотите сгенерировать.

Пример:

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

Ответ 2

Хотя все остальные ответы верны, они сформулированы немного громоздкими.

Map<Integer, Long>, отображает перевернутую монету в счет.

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

Сначала создадим IntStream, а затем привяжите их к Stream<Integer>, так как вы будете хранить их в своей коробке в любом случае в этом примере. И, наконец, собирайте их с помощью функции groupingBy на идентификаторе i -> i, который дает вам Map<Integer, List<Integer>>, который вам не нужен, поэтому вы заменяете List<Integer> на операцию Collectors.counting() на нем, так что List<Integer> становится a Long, что приводит к a Map<Integer, Long>.

Ответ 3

Вы можете собрать несколько результатов в одной итерации, если хотите получить два выхода. В вашем случае это может выглядеть следующим образом:

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