-
Notifications
You must be signed in to change notification settings - Fork 49
1.4. UUIDv4
💡 HINT
What are UUIDs?: https://playfulprogramming.com/posts/what-are-uuids#UUIDv4
Creating a Random-based UUID:
UUID uuid = UuidCreator.getRandomBased();
Sequence of random-based UUIDs:
38b37e02-d978-42cc-b39f-699d41ad6b13
4241488c-c17a-48c9-bf82-aa0afe675c2f
03e26434-323c-411c-bedf-34f8b99889e8
625b9fa3-21d1-4ddc-a5d7-97d277fbe268
b60a97a3-c1f9-48e9-8afe-d8505fd3fe58
071105f2-6c78-4fbb-a5c1-c8f48afd1a76
5bf70214-67e4-4f31-b3fb-cb8a8366d158
1dd86663-2263-443a-a49c-29d6d877b3f4
|------------------------------------|
randomness
The random-based factory uses SecureRandom
to get 'cryptographic quality random' bytes as the standard requires.
If the default SecureRandom
is not desired, any instance of java.util.Random
can be used.
You can also implement a custom RandomFunction
that uses any random generator you like. See the more examples section.
Under certain circumstances, using UUID#randomUUID()
can affect your application’s availability.
This library allows you to select the algorithm used by SecureRandom
to generate random-based UUIDs. It can be done by defining the system property uuidcreator.securerandom
or the environment variable UUIDCREATOR_SECURERANDOM
. The system property has priority over the environment variable. If no property or variable is defined, the algorithm is chosen by the runtime.
It can be useful to make use of SHA1PRNG or DRBG as less-blocking source of random bytes. The SHA1PRNG algorithm is default in some operating systems that don't have '/dev/random' or '/dev/urandom', e.g., in Windows. The DRBG algorithm is available in JDK9+.
- Defining a system property:
# Append one of these examples to VM arguments
# Use the the algorithm SHA1PRNG for SecureRandom
-Duuidcreator.securerandom="SHA1PRNG"
# Use the the algorithm DRBG for SecureRandom (JDK9+)
-Duuidcreator.securerandom="DRBG"
- Defining an environment variable:
# Append one of these examples to /etc/environment or ~/.profile
# Use the the algorithm SHA1PRNG for SecureRandom
export UUIDCREATOR_SECURERANDOM="SHA1PRNG"
# Use the the algorithm DRBG for SecureRandom (JDK9+)
export UUIDCREATOR_SECURERANDOM="DRBG"
This is a 4-threaded benchmark comparing the default algorithm with SHA1PRNG and DRBG:
Benchmark Mode Cnt Score Error Units
DEFAULT thrpt 5 1385,507 ± 67,979 ops/ms
SHA1PRNG thrpt 5 10422,873 ± 378,375 ops/ms
DRBG thrpt 5 2195,322 ± 5,905 ops/ms
Benchmark machine: JDK 9, Ubuntu 20.04, CPU Intel i5-3330 and 8GB RAM.
Creating a quick random-based UUID:
// uses SplittableRandom as random generator
UUID uuid = UuidCreator.getRandomBasedFast();
A key generator that makes substitution easy if necessary:
package com.example;
import com.github.f4b6a3.uuid.UuidCreator;
public class KeyGenerator {
public static String next() {
return UuidCreator.getRandomBased().toString();
}
}
String key = KeyGenerator.next();
A key generator that hides the complexity behind its generation method:
package com.example;
import java.util.Random;
import com.github.f4b6a3.uuid.factory.rfc4122.RandomBasedFactory;
public class KeyGenerator {
private static final RandomBasedFactory FACTORY = new RandomBasedFactory(new Random());
public static String next() {
return FACTORY.create().toString();
}
}
String key = KeyGenerator.next();
A NanoID generator using UuidCreator
and Base64UrlCodec
:
package com.example;
import com.github.f4b6a3.uuid.UuidCreator;
import com.github.f4b6a3.uuid.codec.base.Base64UrlCodec;
public class NanoID {
// NanoID and base-64-url alphabets have the same 64 characters
private static final Base64UrlCodec ENCODER = Base64UrlCodec.INSTANCE;
public static String next() {
// return a random 22-character string
return ENCODER.encode(UuidCreator.getRandomBased());
}
}
String id = NanoID.next();
A random-based factory with Random
:
// a random-based factory with `Random`
Random random = new Random();
RandomBasedFactory factory = new RandomBasedFactory(random);
// use the custom factory
UUID uuid = factory.create();
A random-based factory with SecureRandom
using DRBG algorithm (JDK9+):
// a random-based factory with `SecureRandom` using DRBG algorithm
SecureRandom random = SecureRandom.getInstance("DRBG");
RandomBasedFactory factory = new RandomBasedFactory(random);
// use the custom factory
UUID uuid = factory.create();
A random-based factory with SplittableRandom
:
// use a random function that returns a long value
SplittableRandom random = new SplittableRandom();
RandomBasedFactory factory = RandomBasedFactory(() -> random.nextLong());
// use the factory
UUID uuid = factory.create();
A random-based factory with RandomGenerator
(JDK 17+):
// use a random function that returns a long value
RandomGenerator random = RandomGenerator.getDefault();
RandomBasedFactory factory = RandomBasedFactory(() -> random.nextLong());
// use the factory
UUID uuid = factory.create();
A random-based factory with ThreadLocalRandom
:
// a random-based factory with a custom `RandomFunction`
RandomBasedFactory factory = new RandomBasedFactory((int length) -> {
final byte[] bytes = new byte[length];
ThreadLocalRandom.current().nextBytes(bytes);
return bytes;
});
// use the custom factory
UUID uuid = factory.create();
A less-blocking factory that wraps an array of random-based factories to generate UUIDs with less thread contention:
package com.example;
import java.util.UUID;
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
import com.github.f4b6a3.uuid.factory.NoArgsFactory;
import com.github.f4b6a3.uuid.factory.function.RandomFunction;
import com.github.f4b6a3.uuid.factory.rfc4122.RandomBasedFactory;
/**
* A less-blocking factory that wraps an array of factories.
*
* It can be used to generate UUIDs with less thread contention.
*
* It is unnecessary to use it with the default {@link RandomBasedFactory}
* (instantiated without arguments), unless you want to pass your own
* {@link Random} or {@link RandomFunction} argument to the constructor.
*/
public class LessBlockingFactory implements NoArgsFactory {
private final NoArgsFactory[] factories;
public LessBlockingFactory(int length) {
factories = new NoArgsFactory[length];
try {
for (int i = 0; i < factories.length; i++) {
// Passing as argument a SecureRandom with SHA1PRNG algorithm.
// SHA1PRNG or DRBG can be used to avoid contention from the default SecureRandom.
SecureRandom argument = SecureRandom.getInstance("SHA1PRNG");
factories[i] = new RandomBasedFactory(argument);
}
} catch (NoSuchAlgorithmException e) {
// oops!
}
}
@Override
public UUID create() {
// calculate the factory index given the current thread ID
final int index = (int) Thread.currentThread().getId() % factories.length;
return factories[index].create();
}
}
// instantiate a less-blocking factory with an array of 8 factories
LessBlockingFactory factory = new LessBlockingFactory(8);
// use the less-blocking factory
UUID uuid = factory.create();