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