Статьи

Случайный в Java 8

Одна из замечательных особенностей класса 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

Ссылка: java.util.Random в Java 8 от нашего партнера по JCG Биджу Кунджуммен в блоге all and sundry.