Generate CUIDs in Java

Resources  |  Generate CUIDs in Java

Generating a CUID (Collision-resistant Unique Identifier) in Java involves using various techniques to ensure uniqueness, including using the current timestamp, a counter, a machine fingerprint, and randomness. Below is an example implementation of generating a CUID in Java.

Steps to Generate a CUID in Java

  1. Timestamp: Get the current timestamp in milliseconds.
  2. Counter: Use a counter to handle multiple CUIDs generated in a short period.
  3. Fingerprint: Generate a machine-specific fingerprint.
  4. Randomness: Add random characters to further reduce the risk of collisions.
  5. Base36 Encoding: Encode the components using Base36 for readability.

Example Code

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class CUIDGenerator {
    private static final char[] BASE36_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray();
    private static final AtomicInteger COUNTER = new AtomicInteger(0);
    private static long lastTimestamp = -1L;

    public static String generateCUID() {
        long timestamp = System.currentTimeMillis();
        
        int counterValue;
        synchronized (CUIDGenerator.class) {
            if (timestamp == lastTimestamp) {
                counterValue = COUNTER.incrementAndGet();
            } else {
                lastTimestamp = timestamp;
                COUNTER.set(0);
                counterValue = COUNTER.get();
            }
        }

        String timestampBase36 = encodeBase36(timestamp);
        String counterBase36 = encodeBase36(counterValue);
        String fingerprint = getMachineFingerprint();
        String randomString = getRandomString(4);

        return "c" + timestampBase36 + counterBase36 + fingerprint + randomString;
    }

    private static String encodeBase36(long value) {
        StringBuilder result = new StringBuilder();
        while (value > 0) {
            result.insert(0, BASE36_CHARS[(int) (value % 36)]);
            value /= 36;
        }
        return result.toString();
    }

    private static String getMachineFingerprint() {
        try {
            InetAddress ip = InetAddress.getLocalHost();
            String hostname = ip.getHostName();
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashBytes = md.digest(hostname.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(BASE36_CHARS[(b & 0xFF) % 36]);
            }
            return sb.toString().substring(0, 4);
        } catch (UnknownHostException | NoSuchAlgorithmException e) {
            throw new RuntimeException("Failed to get machine fingerprint", e);
        }
    }

    private static String getRandomString(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(BASE36_CHARS[random.nextInt(36)]);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(generateCUID());
    }
}

Explanation

  1. Base36 Encoding:

    • The encodeBase36 method converts a number to a Base36 encoded string.
  2. Timestamp:

    • long timestamp = System.currentTimeMillis(); gets the current timestamp in milliseconds.
  3. Counter:

    • An AtomicInteger counter is used to handle multiple CUIDs generated within the same millisecond. The counter is incremented and reset in a thread-safe manner.
  4. Machine Fingerprint:

    • getMachineFingerprint generates a fingerprint using the MD5 hash of the machine's hostname.
  5. Random String:

    • getRandomString generates a random string of specified length using Base36 characters.
  6. Combine Components:

    • The generateCUID method combines the timestamp, counter, fingerprint, and random parts to form the final CUID.

Summary

This Java implementation of CUID generation uses available functions and libraries to handle timestamps, hashing, randomness, and thread-safe counters. This approach ensures that the generated CUIDs are unique, readable, and collision-resistant, making them suitable for various applications.