在Java中创建GUID / UUID

445

在Java中创建GUID / UUID的最佳方法有哪些?


3
这个问题的重点不是如何在Java技术栈中生成GUID的等价物,而是关于GUID冲突的问题,这也是https://dev59.com/FHRC5IYBdhLWcg3wW_pk问题更加关注的内容(我认为)。 - Jon Adams
7个回答

499

java.util.UUID.randomUUID();


2
它能返回重复吗?因为Guid永远不会重复。 - angel
6
@angel 是的,UUID.randomUUID 方法在理论上可能会返回一个重复的 UUID,但这并不是一个现实中需要担心的问题。 Oracle/OpenJDK 实现使用了一个具有密码强度的随机数生成器。考虑到 UUID 中包含如此多的位数,提供了一个天文数字范围,你的应用程序可以生成数百万个这样的值而不会出现重复。如果使用其他变量,如 [1] MAC 地址或名称和 [2] 当前日期时间作为限制条件,则进一步将碰撞的可能性降至接近零。 - Basil Bourque
1
@RenniePet 嗯,如果你这么担心,并且在创建新的ID时已经可以访问已使用ID列表,那么你只需在while中生成新的ID,直到找到一个不在列表中的ID为止 :p - Nyerguds
2
Oracle加密随机数生成器通常是带有随机种子的伪随机数生成器(PRNG)。随机种子通常使用操作系统提供的“熵”源获取。如果您可以降级或危害该源,则加密随机数生成器产生相同数字的概率会增加。值得注意的是,在某些(例如虚拟化)平台上,操作系统可能会耗尽熵。有一些“不怎么靠谱的黑科技”可以解决此问题,但这会降低熵的质量。 - Stephen C
这不仅仅是学术上的关注。我看到有人(未经验证!)声称使用基于随机数的UUID时遇到了唯一性问题。 - Stephen C

391

请查看随Java 5及更高版本一起捆绑的UUID类

例如:


10
如果你像Kaleb Brasee一样提供一个例子,那么你的好回答将会更加优秀。 - Zero3
适用于AndroidStudio 2.3及API级别21及以上,至少可以向后兼容。也可能支持更早的版本。 - raddevus
8
UUID uuid = UUID.randomUUID(); 的意思是生成一个随机的UUID。 - Aviram Fireberger

39

仅通过示例扩展Mark Byers的回答:

import java.util.UUID;

public class RandomStringUUID {
    public static void main(String[] args) {
        UUID uuid = UUID.randomUUID();
        System.out.println("UUID=" + uuid.toString() );
    }
}

1
因为没有提供JSF、Spring MVC、Android和Swing的“扩展版本”,所以被踩了。拜托,为什么要提供这样的“答案”? - terrorrussia-keeps-killing
1
@fluffy,这个问题在哪里要求的? - Anton Belev

33

这取决于你想要哪种类型的UUID

  • 标准的Java UUID类生成版本4(随机)UUID。(更新 - 也可以生成版本3(名称)UUID。) 它还可以处理其他变体,尽管它不能生成它们。(在这种情况下,“处理”意味着从longbyte[]String表示构造UUID实例,并提供一些适当的访问器。)

  • Java UUID Generator (JUG)实现声称支持“由RFC-4122定义的所有3种‘官方’类型的UUID”...尽管RFC实际上定义了4种类型并提到了第5种类型。

如果想了解UUID类型和变体的更多信息,可以参考Wikipedia提供的良好概述,而详细信息则在RFC 4122和其他规范中。


1
并不完全正确,它还可以使用UUID.nameUUIDFromBytes(byte[] name)生成基于名称(版本3)的UUID。 - DennisK
我改正了。我依赖于javadoc的描述,它说:“静态工厂用于基于指定的字节数组检索类型3(基于名称的)UUID。” - Stephen C
我认为这篇文章不准确。UUID Java文档指出构造函数生成的是类型2变体,而不是随机数。要使用类型3,请使用public static UUID nameUUIDFromBytes(byte[] name)。要使用类型4,请使用public static UUID randomUUID()。JDK类中不提供类型1。 - slartibartfast
不,我认为我基本上是正确的。Javadocs指出:“尽管构造函数允许创建UUID的任何变体(如下所述)。”并且列出了4个变体,包括类型1。确实,阅读clockSequence()的javadoc。 - Stephen C

8

其他答案都是正确的,特别是这个Stephen C提供。

扩展到Java之外

在Java中生成UUID值受到安全问题的限制,只能使用版本4(随机)

如果你需要其他版本的UUID,一种方法是让你的Java应用程序通过调用以下内容来生成UUID,以便超出JVM
  • 命令行实用程序
    几乎与每个操作系统捆绑在一起。
    例如,在Mac OS X、BSD和Linux中可以找到uuidgen
  • 数据库服务器
    使用JDBC检索在数据库服务器上生成的UUID。
    例如,通常与Postgres捆绑的uuid-ossp扩展可以生成版本1、3和4值以及其他几种变体:
  • uuid_generate_v1mc() - 生成版本1 UUID,但使用随机组播MAC地址而不是计算机的真实MAC地址。
  • uuid_generate_v5(namespace uuid, name text) - 生成版本5 UUID,其工作方式类似于版本3 UUID,但使用SHA-1作为哈希方法。
  • Web服务
    例如,UUID Generator创建版本1和3以及nil valuesGUID

5
我对你的回答有一些问题:首先,已经证明你不仅可以从标准Java库中获取V4,还可以获取V3。其次,你让它听起来就像除了标准库之外,在Java中没有其他选项,并且因为安全问题而模棱两可。最后,当有很多在Java内部执行的方法时(除非需要在这些方法中执行,例如创建SQL服务器记录的一部分),依赖于外部来源通常会导致低效率(编程和/或性能方面)。 - DennisK

6
这个回答包含两个随机和基于名称的UUID生成器,符合RFC-4122标准。请自由使用和分享。
随机生成的UUID (v4)
这个实用类生成随机的UUID:
package your.package.name;

import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;

/**
 * Utility class that creates random-based UUIDs.
 * 
 */
public abstract class RandomUuidCreator {

    private static final int RANDOM_VERSION = 4;

    /**
     * Returns a random-based UUID.
     * 
     * It uses a thread local {@link SecureRandom}.
     * 
     * @return a random-based UUID
     */
    public static UUID getRandomUuid() {
        return getRandomUuid(SecureRandomLazyHolder.THREAD_LOCAL_RANDOM.get());
    }

    /**
     * Returns a random-based UUID.
     * 
     * It uses any instance of {@link Random}.
     * 
     * @return a random-based UUID
     */
    public static UUID getRandomUuid(Random random) {

        long msb = 0;
        long lsb = 0;

        // (3) set all bit randomly
        if (random instanceof SecureRandom) {
            // Faster for instances of SecureRandom
            final byte[] bytes = new byte[16];
            random.nextBytes(bytes);
            msb = toNumber(bytes, 0, 8); // first 8 bytes for MSB
            lsb = toNumber(bytes, 8, 16); // last 8 bytes for LSB
        } else {
            msb = random.nextLong(); // first 8 bytes for MSB
            lsb = random.nextLong(); // last 8 bytes for LSB
        }

        // Apply version and variant bits (required for RFC-4122 compliance)
        msb = (msb & 0xffffffffffff0fffL) | (RANDOM_VERSION & 0x0f) << 12; // apply version bits
        lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant bits

        // Return the UUID
        return new UUID(msb, lsb);
    }

    private static long toNumber(final byte[] bytes, final int start, final int length) {
        long result = 0;
        for (int i = start; i < length; i++) {
            result = (result << 8) | (bytes[i] & 0xff);
        }
        return result;
    }

    // Holds thread local secure random
    private static class SecureRandomLazyHolder {
        static final ThreadLocal<Random> THREAD_LOCAL_RANDOM = ThreadLocal.withInitial(SecureRandom::new);
    }

    /**
     * For tests!
     */
    public static void main(String[] args) {

        System.out.println("// Using thread local `java.security.SecureRandom` (DEFAULT)");
        System.out.println("RandomUuidCreator.getRandomUuid()");
        System.out.println();
        for (int i = 0; i < 5; i++) {
            System.out.println(RandomUuidCreator.getRandomUuid());
        }

        System.out.println();
        System.out.println("// Using `java.util.Random` (FASTER)");
        System.out.println("RandomUuidCreator.getRandomUuid(new Random())");
        System.out.println();
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            System.out.println(RandomUuidCreator.getRandomUuid(random));
        }
    }
}

这是输出结果:

// Using thread local `java.security.SecureRandom` (DEFAULT)
RandomUuidCreator.getRandomUuid()

'ef4f5ad2-8147-46cb-8389-c2b8c3ef6b10'
'adc0305a-df29-4f08-9d73-800fde2048f0'
'4b794b59-bff8-4013-b656-5d34c33f4ce3'
'22517093-ee24-4120-96a5-ecee943992d1'
'899fb1fb-3e3d-4026-85a8-8a2d274a10cb'

// Using `java.util.Random` (FASTER)
RandomUuidCreator.getRandomUuid(new Random())

'4dabbbc2-fcb2-4074-a91c-5e2977a5bbf8'
'078ec231-88bc-4d74-9774-96c0b820ceda'
'726638fa-69a6-4a18-b09f-5fd2a708059b'
'15616ebe-1dfd-4f5c-b2ed-cea0ac1ad823'
'affa31ad-5e55-4cde-8232-cddd4931923a'

基于名称的UUID(v3和v5)

这个实用程序类可以生成基于名称的UUID(MD5和SHA1):

package your.package.name;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * Utility class that creates UUIDv3 (MD5) and UUIDv5 (SHA1).
 *
 */
public class HashUuidCreator {

    // Domain Name System
    public static final UUID NAMESPACE_DNS = new UUID(0x6ba7b8109dad11d1L, 0x80b400c04fd430c8L);
    // Uniform Resource Locator
    public static final UUID NAMESPACE_URL = new UUID(0x6ba7b8119dad11d1L, 0x80b400c04fd430c8L);
    // ISO Object ID
    public static final UUID NAMESPACE_ISO_OID = new UUID(0x6ba7b8129dad11d1L, 0x80b400c04fd430c8L);
    // X.500 Distinguished Name
    public static final UUID NAMESPACE_X500_DN = new UUID(0x6ba7b8149dad11d1L, 0x80b400c04fd430c8L);

    private static final int VERSION_3 = 3; // UUIDv3 MD5
    private static final int VERSION_5 = 5; // UUIDv5 SHA1

    private static final String MESSAGE_DIGEST_MD5 = "MD5"; // UUIDv3
    private static final String MESSAGE_DIGEST_SHA1 = "SHA-1"; // UUIDv5

    private static UUID getHashUuid(UUID namespace, String name, String algorithm, int version) {

        final byte[] hash;
        final MessageDigest hasher;

        try {
            // Instantiate a message digest for the chosen algorithm
            hasher = MessageDigest.getInstance(algorithm);

            // Insert name space if NOT NULL
            if (namespace != null) {
                hasher.update(toBytes(namespace.getMostSignificantBits()));
                hasher.update(toBytes(namespace.getLeastSignificantBits()));
            }

            // Generate the hash
            hash = hasher.digest(name.getBytes(StandardCharsets.UTF_8));

            // Split the hash into two parts: MSB and LSB
            long msb = toNumber(hash, 0, 8); // first 8 bytes for MSB
            long lsb = toNumber(hash, 8, 16); // last 8 bytes for LSB

            // Apply version and variant bits (required for RFC-4122 compliance)
            msb = (msb & 0xffffffffffff0fffL) | (version & 0x0f) << 12; // apply version bits
            lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant bits

            // Return the UUID
            return new UUID(msb, lsb);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Message digest algorithm not supported.");
        }
    }

    public static UUID getMd5Uuid(String string) {
        return getHashUuid(null, string, MESSAGE_DIGEST_MD5, VERSION_3);
    }

    public static UUID getSha1Uuid(String string) {
        return getHashUuid(null, string, MESSAGE_DIGEST_SHA1, VERSION_5);
    }

    public static UUID getMd5Uuid(UUID namespace, String string) {
        return getHashUuid(namespace, string, MESSAGE_DIGEST_MD5, VERSION_3);
    }

    public static UUID getSha1Uuid(UUID namespace, String string) {
        return getHashUuid(namespace, string, MESSAGE_DIGEST_SHA1, VERSION_5);
    }

    private static byte[] toBytes(final long number) {
        return new byte[] { (byte) (number >>> 56), (byte) (number >>> 48), (byte) (number >>> 40),
                (byte) (number >>> 32), (byte) (number >>> 24), (byte) (number >>> 16), (byte) (number >>> 8),
                (byte) (number) };
    }

    private static long toNumber(final byte[] bytes, final int start, final int length) {
        long result = 0;
        for (int i = start; i < length; i++) {
            result = (result << 8) | (bytes[i] & 0xff);
        }
        return result;
    }

    /**
     * For tests!
     */
    public static void main(String[] args) {

        String string = "JUST_A_TEST_STRING";
        UUID namespace = UUID.randomUUID(); // A custom name space

        System.out.println("Java's generator");
        System.out.println("UUID.nameUUIDFromBytes():      '" + UUID.nameUUIDFromBytes(string.getBytes()) + "'");
        System.out.println();
        System.out.println("This generator");
        System.out.println("HashUuidCreator.getMd5Uuid():  '" + HashUuidCreator.getMd5Uuid(string) + "'");
        System.out.println("HashUuidCreator.getSha1Uuid(): '" + HashUuidCreator.getSha1Uuid(string) + "'");
        System.out.println();
        System.out.println("This generator WITH name space");
        System.out.println("HashUuidCreator.getMd5Uuid():  '" + HashUuidCreator.getMd5Uuid(namespace, string) + "'");
        System.out.println("HashUuidCreator.getSha1Uuid(): '" + HashUuidCreator.getSha1Uuid(namespace, string) + "'");
    }
}

这是输出结果:

// Java's generator
UUID.nameUUIDFromBytes():      '9e120341-627f-32be-8393-58b5d655b751'

// This generator
HashUuidCreator.getMd5Uuid():  '9e120341-627f-32be-8393-58b5d655b751'
HashUuidCreator.getSha1Uuid(): 'e4586bed-032a-5ae6-9883-331cd94c4ffa'

// This generator WITH name space
HashUuidCreator.getMd5Uuid():  '2b098683-03c9-3ed8-9426-cf5c81ab1f9f'
HashUuidCreator.getSha1Uuid(): '1ef568c7-726b-58cc-a72a-7df173463bbb'

备用生成器

您还可以使用uuid-creator库。请查看以下示例:

// Create a random-based UUID
UUID uuid = UuidCreator.getRandomBased();

// Create a name based UUID (SHA1)
String name = "JUST_A_TEST_STRING";
UUID uuid = UuidCreator.getNameBasedSha1(name);

项目页面:https://github.com/f4b6a3/uuid-creator


2

对于许多情况,我们需要为对象提供全局UUID,特别是在事件驱动架构或事件源中,我们必须根据日期对事件进行排序,但我们不需要完整的时间戳信息。

在这种情况下,我们可以使用ULID的实现之一,它是按字典顺序可排序的。

该格式与标准UUID不同,但仍然很简单:

example value: 01AN4Z07BY79KA1307SR9X4MV3

 01AN4Z07BY      79KA1307SR9X4MV3

|----------|    |----------------|
 Timestamp          Randomness
   48bits             80bits

有许多语言都有实现。例如,在Java中,有一个简单的可以实现这个功能。

代码示例:

import de.huxhorn.sulky.ulid.ULID;

ULID ulid = new ULID();

// with current timestamp
String newId = ulid.nextULID(); 

// with selected timestamp
String newId2 = ulid.nextULID(Instant
    .parse("2021-12-01T00:00:00.00Z")
    .toEpochMilli()
); 

使用Spring,您也可以创建用于ULID生成器的Bean。
@Configuration
public class UUIDGeneratorConfig {

    @Bean
    public ULID ulidGenerator() {
        return new ULID();
    }
}

@Component
public class ULIDGenerator {

    private final ULID ulid;

    public ULIDGenerator(ULID ulid) {
        this.ulid = ulid;
    }

    public String generateUUID() {
        return ulid.nextULID();
    }

    public String generateUUID(Instant timestamp) {
        return ulid.nextULID(timestamp.toEpochMilli());
    }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接