All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.util.random.RandomGeneratorFactory Maven / Gradle / Ivy

There is a newer version: 17.alpha.0.57
Show newest version
/*
 * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.util.random;

import java.lang.reflect.Constructor;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Objects;
import java.util.function.Function;
import java.util.Map;
import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator;
import java.util.random.RandomGenerator.JumpableGenerator;
import java.util.random.RandomGenerator.LeapableGenerator;
import java.util.random.RandomGenerator.SplittableGenerator;
import java.util.random.RandomGenerator.StreamableGenerator;
import java.util.ServiceLoader;
import java.util.ServiceLoader.Provider;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;

/**
 * This is a factory class for generating multiple random number generators
 * of a specific algorithm.
 * {@link RandomGeneratorFactory} also provides
 * methods for selecting random number generator algorithms.
 *
 * A specific {@link RandomGeneratorFactory} can be located by using the
 * {@link RandomGeneratorFactory#of(String)} method, where the argument string
 * is the name of the algorithm
 * required. The method
 * {@link RandomGeneratorFactory#all()} produces a non-empty {@link Stream} of all available
 * {@link RandomGeneratorFactory RandomGeneratorFactorys} that can be searched
 * to locate a {@link RandomGeneratorFactory} suitable to the task.
 *
 * There are three methods for constructing a RandomGenerator instance,
 * depending on the type of initial seed required.
 * {@link RandomGeneratorFactory#create(long)} is used for long
 * seed construction,
 * {@link RandomGeneratorFactory#create(byte[])} is used for byte[]
 * seed construction, and
 * {@link RandomGeneratorFactory#create()} is used for random seed
 * construction. Example;
 *
 * 
{@code
 *    RandomGeneratorFactory factory = RandomGeneratorFactory.of("Random");
 *
 *     for (int i = 0; i < 10; i++) {
 *         new Thread(() -> {
 *             RandomGenerator random = factory.create(100L);
 *             System.out.println(random.nextDouble());
 *         }).start();
 *     }
 * }
* * RandomGeneratorFactory also provides methods describing the attributes (or properties) * of a generator and can be used to select random number generator * algorithms. * These methods are typically used in * conjunction with {@link RandomGeneratorFactory#all()}. In this example, the code * locates the {@link RandomGeneratorFactory} that produces * {@link RandomGenerator RandomGenerators} * with the highest number of state bits. * *
{@code
 *     RandomGeneratorFactory best = RandomGeneratorFactory.all()
 *         .sorted(Comparator.comparingInt(RandomGenerator::stateBits).reversed())
 *         .findFirst()
 *         .orElse(RandomGeneratorFactory.of("Random"));
 *     System.out.println(best.name() + " in " + best.group() + " was selected");
 *
 *     RandomGenerator rng = best.create();
 *     System.out.println(rng.nextLong());
 * }
* * @since 17 * * @see java.util.random * */ public final class RandomGeneratorFactory { /** * Instance provider class of random number algorithm. */ private final Provider provider; /** * Provider RandomGeneratorProperties annotation. */ private volatile RandomGeneratorProperties properties; /** * Default provider constructor. */ private volatile Constructor ctor; /** * Provider constructor with long seed. */ private Constructor ctorLong; /** * Provider constructor with byte[] seed. */ private Constructor ctorBytes; private static class FactoryMapHolder { static final Map> FACTORY_MAP = createFactoryMap(); /** * Returns the factory map, lazily constructing map on first use. * * @return Map of RandomGeneratorFactory classes. */ private static Map> createFactoryMap() { return ServiceLoader .load(RandomGenerator.class) .stream() .filter(p -> !p.type().isInterface()) .collect(Collectors.toMap(p -> p.type().getSimpleName(), Function.identity())); } } /** * Private constructor. * * @param provider Provider class to wrap. */ private RandomGeneratorFactory(Provider provider) { this.provider = provider; } /** * Returns the factory map, lazily constructing map on first call. * * @return Map of RandomGeneratorFactory classes. */ private static Map> getFactoryMap() { return FactoryMapHolder.FACTORY_MAP; } /** * Return the annotation for the specified provider. * * @return RandomGeneratorProperties annotation for the specified provider. */ private RandomGeneratorProperties getProperties() { if (properties == null) { synchronized (provider) { if (properties == null) { properties = provider.type().getDeclaredAnnotation(RandomGeneratorProperties.class); Objects.requireNonNull(properties, provider.type() + " missing annotation"); } } } return properties; } /** * Return true if the provider is a subclass of the category. * * @param category Interface category, sub-interface of {@link RandomGenerator}. * * @return true if the provider is a subclass of the category. */ private boolean isSubclass(Class category) { return isSubclass(category, provider); } /** * Return true if the provider is a subclass of the category. * * @param category Interface category, sub-interface of {@link RandomGenerator}. * @param provider Provider that is being filtered. * * @return true if the provider is a subclass of the category. */ private static boolean isSubclass(Class category, Provider provider) { return provider != null && category.isAssignableFrom(provider.type()); } /** * Returns the provider matching name and category. * * @param name Name of RandomGenerator * @param category Interface category, sub-interface of {@link RandomGenerator}. * * @return A provider matching name and category. * * @throws IllegalArgumentException if provider is not a subclass of category. */ private static Provider findProvider(String name, Class category) throws IllegalArgumentException { Map> fm = getFactoryMap(); Provider provider = fm.get(name); if (provider == null) { throw new IllegalArgumentException("No implementation of the random number generator algorithm \"" + name + "\" is available"); } else if (!isSubclass(category, provider)) { throw new IllegalArgumentException("The random number generator algorithm \"" + name + "\" is not implemented with the interface \"" + category.getSimpleName() + "\""); } return provider; } /** * Returns a {@link RandomGenerator} that utilizes the {@code name} * algorithm. * * @param name Name of random number algorithm to use * @param category Sub-interface of {@link RandomGenerator} to type check * @param Sub-interface of {@link RandomGenerator} to produce * * @return An instance of {@link RandomGenerator} * * @throws IllegalArgumentException when either the name or category is null */ static T of(String name, Class category) throws IllegalArgumentException { @SuppressWarnings("unchecked") T uncheckedRandomGenerator = (T)findProvider(name, category).get(); return uncheckedRandomGenerator; } /** * Returns a {@link RandomGeneratorFactory} that will produce instances * of {@link RandomGenerator} that utilizes the named algorithm. * * @param name Name of random number algorithm to use * @param category Sub-interface of {@link RandomGenerator} to type check * @param Sub-interface of {@link RandomGenerator} to produce * * @return Factory of {@link RandomGenerator} * * @throws IllegalArgumentException when either the name or category is null */ static RandomGeneratorFactory factoryOf(String name, Class category) throws IllegalArgumentException { Provider uncheckedProvider = findProvider(name, category); return new RandomGeneratorFactory<>(uncheckedProvider); } /** * Fetch the required constructors for class of random number algorithm. * * @param randomGeneratorClass class of random number algorithm (provider) */ private void getConstructors(Class randomGeneratorClass) { if (ctor == null) { synchronized (provider) { if (ctor == null) { PrivilegedExceptionAction[]> ctorAction = randomGeneratorClass::getConstructors; try { @SuppressWarnings("removal") Constructor[] ctors = AccessController.doPrivileged(ctorAction); Constructor tmpCtor = null; Constructor tmpCtorLong = null; Constructor tmpCtorBytes = null; for (Constructor ctorGeneric : ctors) { @SuppressWarnings("unchecked") Constructor ctorSpecific = (Constructor) ctorGeneric; final Class[] parameterTypes = ctorSpecific.getParameterTypes(); if (parameterTypes.length == 0) { tmpCtor = ctorSpecific; } else if (parameterTypes.length == 1) { Class argType = parameterTypes[0]; if (argType == long.class) { tmpCtorLong = ctorSpecific; } else if (argType == byte[].class) { tmpCtorBytes = ctorSpecific; } } } if (tmpCtor == null) { throw new IllegalStateException("Random algorithm " + name() + " is missing a default constructor"); } // Store specialized constructors first, guarded by ctor ctorBytes = tmpCtorBytes; ctorLong = tmpCtorLong; ctor = tmpCtor; } catch (PrivilegedActionException ex) { // Do nothing } } } } } /** * Ensure all the required constructors are fetched. */ private void ensureConstructors() { getConstructors(provider.type()); } /** * Returns a {@link RandomGeneratorFactory} that can produce instances of * {@link RandomGenerator} that utilize the {@code name} * algorithm. * * @implSpec Availability is determined by RandomGeneratorFactory using the * service provider API to locate implementations of the RandomGenerator interface. * * @param name Name of random number generator * algorithm * @param Sub-interface of {@link RandomGenerator} to produce * * @return {@link RandomGeneratorFactory} of {@link RandomGenerator} * * @throws NullPointerException if name is null * @throws IllegalArgumentException if the named algorithm is not found */ public static RandomGeneratorFactory of(String name) { Objects.requireNonNull(name); @SuppressWarnings("unchecked") RandomGeneratorFactory factory = (RandomGeneratorFactory)factoryOf(name, RandomGenerator.class); return factory; } /** * Returns a {@link RandomGeneratorFactory} meeting the minimal requirement * of having an algorithm whose state bits are greater than or equal 64. * * @implSpec Since algorithms will improve over time, there is no * guarantee that this method will return the same algorithm over time. * * @return a {@link RandomGeneratorFactory} */ public static RandomGeneratorFactory getDefault() { return factoryOf("L32X64MixRandom", RandomGenerator.class); } /** * Returns a non-empty stream of available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}. * * RandomGenerators that are marked as deprecated are not included in the result. * * @implSpec Availability is determined by RandomGeneratorFactory using the service provider API * to locate implementations of the RandomGenerator interface. * * @return a non-empty stream of all available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}. */ public static Stream> all() { Map> fm = getFactoryMap(); return fm.values() .stream() .filter(p -> !p.type().isAnnotationPresent(Deprecated.class) && p.type().isAnnotationPresent(RandomGeneratorProperties.class)) .map(RandomGeneratorFactory::new); } /** * Return the name of the algorithm * used by the random number generator. * * @return Name of the algorithm. */ public String name() { return provider.type().getSimpleName(); } /** * Return the group name of the algorithm * used by the random number generator. * * @return Group name of the algorithm. */ public String group() { return getProperties().group(); } /** * Returns number of bits used by the algorithm * to maintain state of seed. * * @return number of bits used by the algorithm * to maintain state of seed. */ public int stateBits() { RandomGeneratorProperties properties = getProperties(); int i = properties.i(); int k = properties.k(); return i == 0 && k == 0 ? Integer.MAX_VALUE : i + k; } /** * Returns the equidistribution of the algorithm. * * @return the equidistribution of the algorithm. */ public int equidistribution() { return getProperties().equidistribution(); } /** * Return the period of the algorithm * used by the random number generator. * Returns BigInteger.ZERO if period is not determinable. * * @return BigInteger period. */ public BigInteger period() { RandomGeneratorProperties properties = getProperties(); int i = properties.i(); int j = properties.j(); int k = properties.k(); if (i == 0 && j == 0 && k == 0) { return BigInteger.ZERO; } else { return BigInteger.ONE.shiftLeft(i).subtract(BigInteger.valueOf(j)).shiftLeft(k); } } /** * Return true if random generator is computed using an arithmetic * algorithm * and is statistically deterministic. * * @return true if random generator is statistical. */ public boolean isStatistical() { return !getProperties().isStochastic(); } /** * Return true if random generator is computed using external or entropic * sources as inputs. * * @return true if random generator is stochastic. */ public boolean isStochastic() { return getProperties().isStochastic(); } /** * Return true if random generator uses a hardware device (HRNG) to produce * entropic input. * * @return true if random generator is generated by hardware. */ public boolean isHardware() { return getProperties().isHardware(); } /** * Return true if random generator can jump an arbitrarily specified distant * point in the state cycle. * * @return true if random generator is arbitrarily jumpable. */ public boolean isArbitrarilyJumpable() { return isSubclass(ArbitrarilyJumpableGenerator.class); } /** * Return true if random generator can jump a specified distant point in * the state cycle. * * @return true if random generator is jumpable. */ public boolean isJumpable() { return isSubclass(JumpableGenerator.class); } /** * Return true if random generator is jumpable and can leap to a very distant * point in the state cycle. * * @return true if random generator is leapable. */ public boolean isLeapable() { return isSubclass(LeapableGenerator.class); } /** * Return true if random generator can be cloned into a separate object with * the same properties but positioned further in the state cycle. * * @return true if random generator is splittable. */ public boolean isSplittable() { return isSubclass(SplittableGenerator.class); } /** * Return true if random generator can be used to create * {@link java.util.stream.Stream Streams} of random numbers. * * @return true if random generator is streamable. */ public boolean isStreamable() { return isSubclass(StreamableGenerator.class); } /** * Return true if the implementation of RandomGenerator (algorithm) has been * marked for deprecation. * * @implNote Random number generator algorithms evolve over time; new * algorithms will be introduced and old algorithms will * lose standing. If an older algorithm is deemed unsuitable * for continued use, it will be marked as deprecated to indicate * that it may be removed at some point in the future. * * @return true if the implementation of RandomGenerator (algorithm) has been * marked for deprecation */ public boolean isDeprecated() { return provider.type().isAnnotationPresent(Deprecated.class); } /** * Create an instance of {@link RandomGenerator} based on * algorithm chosen. * * @return new in instance of {@link RandomGenerator}. * */ public T create() { try { ensureConstructors(); return ctor.newInstance(); } catch (Exception ex) { // Should never happen. throw new IllegalStateException("Random algorithm " + name() + " is missing a default constructor", ex); } } /** * Create an instance of {@link RandomGenerator} based on * algorithm chosen * providing a starting long seed. If long seed is not supported by an * algorithm then the no argument form of create is used. * * @param seed long random seed value. * * @return new in instance of {@link RandomGenerator}. */ public T create(long seed) { try { ensureConstructors(); return ctorLong.newInstance(seed); } catch (Exception ex) { return create(); } } /** * Create an instance of {@link RandomGenerator} based on * algorithm chosen * providing a starting byte[] seed. If byte[] seed is not supported by an * algorithm then the no * argument form of create is used. * * @param seed byte array random seed value. * * @return new in instance of {@link RandomGenerator}. * * @throws NullPointerException if seed is null. */ public T create(byte[] seed) { Objects.requireNonNull(seed, "seed must not be null"); try { ensureConstructors(); return ctorBytes.newInstance(seed); } catch (Exception ex) { return create(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy