1. Обзор
В этом руководстве мы рассмотрим различные способы генерации случайных чисел в Java.
2. Использование Java-API
Java API предоставляет нам несколько способов достижения нашей цели. Давайте посмотрим на некоторые из них.
2.1. java.lang.Math
Случайный метод класса
Math
вернет двойное
значение в диапазоне от 0,0 (включительно) до 1,0 (не включая). Давайте посмотрим, как мы будем использовать его для получения случайного числа в заданном диапазоне, определяемом min
и max
:
int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);
2.2. java.util. Случайный
До Java 1.7 самым популярным способом генерации случайных чисел было использование nextInt
. Было два способа использования этого метода, с параметрами и без них. Вызов без параметров возвращает любое из значений int
с примерно равной вероятностью. Итак, очень вероятно, что мы получим отрицательные числа:
Random random = new Random();
int randomWithNextInt = random.nextInt();
Если мы используем вызов netxInt
с привязанным
параметром, мы получим числа в пределах диапазона:
int randomWintNextIntWithinARange = random.nextInt(max - min) + min;
Это даст нам число от 0 (включительно) до параметра (исключительно). Таким образом, связанный параметр должен быть больше 0. В противном случае мы получим исключение java.lang.IllegalArgumentException
.
В Java 8 появились новые методы ints
, которые возвращают java.util.stream.IntStream.
Давайте посмотрим, как их использовать.
Метод ints
без параметров возвращает неограниченный поток значений int
:
IntStream unlimitedIntStream = random.ints();
Мы также можем передать один параметр, чтобы ограничить размер потока:
IntStream limitedIntStream = random.ints(streamSize);
И, конечно же, мы можем установить максимум и минимум для генерируемого диапазона:
IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);
2.3. java.util.concurrent.ThreadLocalRandom
В версии Java 1.7 появился новый и более эффективный способ генерации случайных чисел с помощью класса ThreadLocalRandom .
Он имеет три важных отличия от класса Random :
- Нам не нужно явно инициировать новый экземпляр
ThreadLocalRandom
. Это помогает нам избежать ошибок, связанных с созданием множества бесполезных экземпляров и тратой времени на сборщик мусора. - Мы не можем установить начальное значение для
ThreadLocalRandom
, что может привести к реальной проблеме. Если нам нужно установить начальное число, нам следует избегать этого способа генерации случайных чисел. Случайный
класс плохо работает в многопоточных средах
Теперь давайте посмотрим, как это работает:
int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);
С Java 8 или выше у нас появились новые возможности. Во-первых, у нас есть два варианта метода nextInt
:
int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt();
int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);
Во-вторых, что более важно, мы можем использовать метод ints
:
IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();
2.4. java.util.SplittableRandom
В Java 8 также появился очень быстрый генератор — класс SplittableRandom
.
Как видно из JavaDoc, это генератор для использования в параллельных вычислениях. Важно знать, что экземпляры не потокобезопасны. Итак, мы должны соблюдать осторожность при использовании этого класса.
У нас есть методы nextInt
и ints
. С помощью nextInt
мы можем напрямую установить верхний и нижний диапазон, используя вызов двух параметров:
SplittableRandom splittableRandom = new SplittableRandom();
int randomWithSplittableRandom = splittableRandom.nextInt(min, max);
Этот способ использования проверяет, что максимальный
параметр больше минимального
. В противном случае мы получим исключение IllegalArgumentException
. Однако он не проверяет, работаем ли мы с положительными или отрицательными числами. Таким образом, любой из параметров может быть отрицательным. Кроме того, у нас есть доступные вызовы с одним и нулевым параметром. Они работают так же, как мы описали ранее.
У нас также есть методы ints .
Это означает, что мы можем легко получить поток значений int
. Чтобы уточнить, мы можем выбрать ограниченный или неограниченный поток. Для ограниченного потока мы можем установить верх и низ для диапазона генерации чисел:
IntStream limitedIntStreamWithinARangeWithSplittableRandom = splittableRandom.ints(streamSize, min, max);
2.5. java.security.SecureRandom
Если у нас есть приложения, чувствительные к безопасности, мы должны рассмотреть возможность использования SecureRandom
. Это криптографически стойкий генератор. Экземпляры, созданные по умолчанию, не используют криптографически случайные начальные значения. Итак, мы должны либо:
- Установить сид — следовательно, сид будет непредсказуемым
- Установите для системного свойства
java.util.secureRandomSeed значение
true .
Этот класс наследуется от java.util.Random
. Итак, нам доступны все способы, которые мы видели выше. Например, если нам нужно получить любое из значений int
, то вызовем nextInt
без параметров:
SecureRandom secureRandom = new SecureRandom();
int randomWithSecureRandom = secureRandom.nextInt();
С другой стороны, если нам нужно установить диапазон, мы можем вызвать его с привязанным
параметром:
int randomWithSecureRandomWithinARange = secureRandom.nextInt(max - min) + min;
Мы должны помнить, что этот способ использования генерирует исключение IllegalArgumentException
, если параметр не больше нуля.
3. Использование сторонних API
Как мы видели, Java предоставляет нам множество классов и методов для генерации случайных чисел. Однако для этой цели также существуют сторонние API.
Мы собираемся взглянуть на некоторые из них.
3.1. org.apache.commons.math3.random.RandomDataGenerator
В математической библиотеке Commons из проекта Apache Commons есть много генераторов. Самый простой и, вероятно, самый полезный — RandomDataGenerator
. Он использует алгоритм Well19937c
для случайной генерации. Однако мы можем предоставить реализацию нашего алгоритма.
Давайте посмотрим, как его использовать. Во-первых, мы должны добавить зависимость:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
Последнюю версию commons-math3
можно найти на Maven Central .
Затем мы можем начать работать с ним:
RandomDataGenerator randomDataGenerator = new RandomDataGenerator();
int randomWithRandomDataGenerator = randomDataGenerator.nextInt(min, max);
3.2. it.unimi.dsi.util.XoRoShiRo128PlusСлучайный
Безусловно, это одна из самых быстрых реализаций генератора случайных чисел. Он был разработан на факультете информационных наук Миланского университета.
Библиотека также доступна в репозиториях Maven Central . Итак, добавим зависимость:
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>dsiutils</artifactId>
<version>2.6.0</version>
</dependency>
Этот генератор наследуется от java.util.Random
. Однако, если мы посмотрим на JavaDoc , мы поймем, что есть только один способ его использования — через метод nextInt
. Прежде всего, этот метод доступен только для вызовов с нулевым и одним параметром. Любые другие вызовы будут напрямую использовать методы java.util.Random .
Например, если мы хотим получить случайное число в диапазоне, мы должны написать:
XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom();
int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;
4. Вывод
Существует несколько способов реализации генерации случайных чисел. Однако лучшего способа не существует. Следовательно, мы должны выбрать тот, который лучше всего соответствует нашим потребностям.
Полный пример можно найти на GitHub .