Skip to content

1.4. UUIDv4

Fabio Lima edited this page Oct 24, 2022 · 6 revisions

Random-based UUID

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

Implementation

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.

Choosing the SecureRandom algorithm

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"

Benchmarking SecureRandom algorithms

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.

More examples

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();