Одна из замечательных особенностей класса java.util.Random в Java 8 заключается в том, что он был модернизирован и теперь возвращает случайный поток чисел.
Например, чтобы генерировать бесконечный поток случайных двойных чисел от 0 (включительно) до 1 (исключая):
1
2
|
Random random = new Random(); DoubleStream doubleStream = random.doubles(); |
или генерировать бесконечный поток целых чисел от 0 (включительно) до 100 (исключая):
1
2
|
Random random = new Random(); IntStream intStream = random.ints( 0 , 100 ); |
Итак, для чего можно использовать этот бесконечный случайный поток, я покажу несколько сценариев, но имейте в виду, что, поскольку это бесконечный поток, любые терминальные операции должны выполняться после того, как поток каким-то образом ограничен, иначе операция не прекратится!
Например, получить поток из 10 случайных целых чисел и вывести их:
1
|
intStream.limit( 10 ).forEach(System.out::println); |
Или создать список из 100 случайных чисел:
1
2
3
4
|
List<Integer> randomBetween0And99 = intStream .limit( 100 ) .boxed() .collect(Collectors.toList()); |
Для гауссовых псевдослучайных значений не существует потокового эквивалента random.doubles (), однако легко придумать тот, который предоставляет Java 8:
1
2
|
Random random = new Random(); DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e); |
Здесь я использую вызов API Stream.generate, передаваемый поставщику, который генерирует следующий гауссиан с уже доступным методом в классе Random .
Так что теперь, чтобы сделать что-то немного более интересное с потоком псевдослучайных двойных чисел и потоком гауссовых псевдослучайных двойных чисел, я хочу получить распределение двойных чисел для каждого из этих двух потоков, ожидая, что Распределение при построении должно быть равномерно распределено для псевдослучайных двойных чисел и должно быть нормальным распределением для гауссовых псевдослучайных двойных чисел.
В следующем коде я создаю такое распределение для миллиона псевдослучайных значений, в нем используется множество средств, предоставляемых новым Java 8 Streams API:
1
2
3
4
5
6
7
8
|
Random random = new Random(); DoubleStream doubleStream = random.doubles(- 1.0 , 1.0 ); LinkedHashMap<Range, Integer> rangeCountMap = doubleStream.limit( 1000000 ) .boxed() .map(Ranges::of) .collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1 ), Ranges::mergeRangeCountMaps); rangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v)); |
и этот код выплевывает данные по этим направлениям:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
-1 49730 -0.9 49931 -0.8 50057 -0.7 50060 -0.6 49963 -0.5 50159 -0.4 49921 -0.3 49962 -0.2 50231 -0.1 49658 0 50177 0.1 49861 0.2 49947 0.3 50157 0.4 50414 0.5 50006 0.6 50038 0.7 49962 0.8 50071 0.9 49695 |
и аналогичным образом генерируют распределение для миллиона гауссовских псевдослучайных значений:
01
02
03
04
05
06
07
08
09
10
11
|
Random random = new Random(); DoubleStream gaussianStream = Stream.generate(random::nextGaussian).mapToDouble(e -> e); LinkedHashMap<Range, Integer> gaussianRangeCountMap = gaussianStream .filter(e -> (e >= - 1.0 && e < 1.0 )) .limit( 1000000 ) .boxed() .map(Ranges::of) .collect(Ranges::emptyRangeCountMap, (m, e) -> m.put(e, m.get(e) + 1 ), Ranges::mergeRangeCountMaps); gaussianRangeCountMap.forEach((k, v) -> System.out.println(k.from() + "\t" + v)); |
Построение данных дает ожидаемый результат:
Для псевдослучайных данных:
И для гауссовых данных:
- Полный код доступен в гисте здесь — https://gist.github.com/bijukunjummen/8129250