【问题标题】:Create a GUID in Java在 Java 中创建 GUID
【发布时间】:2011-02-28 07:00:23
【问题描述】:

在 Java 中创建 GUID 的最佳方法是什么?

【问题讨论】:

标签: java guid


【解决方案1】:

java.util.UUID.randomUUID();

【讨论】:

  • 它会返回重复吗?因为 Guid 永远不会重复
  • @angel 是的,理论上UUID.randomUUID 方法可以返回一个副本,但这根本不是现实问题。 Oracle/OpenJDK 实现使用加密强的随机数生成器。鉴于此,并且考虑到 UUID 中这么多位给出的天文范围,您可以在您的应用程序中生成数百万个这样的值并且仍然睡得很好。由于使用“空间和时间”、[1] MAC 地址或名称以及 [2] 当前日期时间作为约束,使用其他变体之一进一步降低了冲突的可能性,甚至更接近于零。
  • @RenniePet 呃,如果你是那种偏执狂,并且在创建新 ID 时可以访问已使用 ID 的列表,你只需在 while 中生成新 ID,直到你有一个不在您的列表中:p
  • Oracle 加密随机数生成器通常是带有随机种子的 PRNG。随机种子通常是使用操作系统提供的“熵”源获得的。如果您可以降低或破坏该来源,那么加密随机数生成器产生相同数字的概率就会增加。还值得注意的是,在某些(例如虚拟化)平台上,操作系统可能会耗尽熵。有一些“狡猾的黑客”可以解决这个问题,但它们会降低熵的质量。
  • 这不仅仅是学术问题。我看到一个(unverified !!)声称有人确实遇到了基于随机的 UUID 不唯一的问题。
【解决方案2】:

查看与 Java 5 及更高版本捆绑的UUID class

例如:

【讨论】:

  • 如果你包括一个像 Kaleb Brasee 那样的例子,你的好答案会更好。
  • 至少在 AndroidStudio 2.3 和 API 级别 21 及更高版本上工作。也许也更远。
  • TL;DR... " UUID uuid = UUID.randomUUID(); "
【解决方案3】:

只是用一个例子来扩展 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() );
    }
}

【讨论】:

  • 投反对票,因为没有提供 JSF、Spring MVC、Android 和 Swing“扩展版本”。来吧,为什么要提供这样的“答案”?
  • @fluffy 在问题中究竟是在哪里提出的?
【解决方案4】:

这取决于你想要什么样的UUID

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

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

有关 UUID 类型和变体的更多信息,Wikipedia 中有很好的总结,而血淋淋的细节在 RFC 4122 和其他规范中。

【讨论】:

  • 不完全正确,它还可以使用UUID.nameUUIDFromBytes(byte[] name)生成基于名称(版本3)的UUID
  • 我的立场是正确的。我依靠 javadoc 描述,它说 “静态工厂 检索 基于指定字节数组的类型 3(基于名称)UUID。”
  • 我认为帖子不准确。 UUID Java docs 状态构造函数生成类型 2 变体,而不是随机的。对于类型 3,请使用 public static UUID nameUUIDFromBytes(byte[] name)。对于类型 4,请使用 public static UUID randomUUID()。类型 1 不适用于 JDK 类。
  • 没有。我觉得我说得很对。 javadocs 声明:“尽管构造函数允许创建 UUID 的任何变体(如下所述)。” 下面列出了 4 个变体,包括类型 1。确实,请阅读 clockSequence() 的 javadoc /跨度>
【解决方案5】:

其他答案都是正确的,尤其是Stephen Cthis one

到达 Java 之外

出于安全考虑,Java 中的Generating a UUID 值仅限于Version 4 (random)

如果您想要其他版本的 UUID,一种方法是让您的 Java 应用到达 JVM 之外,通过调用以下命令生成 UUID:

  • 命令行实用程序
    几乎与所有操作系统捆绑在一起。
    例如,Mac OS X、BSD 和 Linux 中的 uuidgen
  • 数据库服务器
    使用JDBC 检索在数据库服务器上生成的UUID。
    例如,uuid-ossp 扩展通常与Postgres 捆绑在一起。该扩展可以生成版本 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

【讨论】:

  • 我对您的回答有一些疑问:首先,您只能从标准 Java 库中获取 V4(V3 也是可能的)已经被证明是错误的。其次,您听起来好像在 Java 中除了标准库之外没有其他选择,以及“出于安全考虑”而手忙脚乱。最后,当在 Java 中有很多方法可以做到这一点时,开始依赖外部资源通常是低效的(编程和/或性能方面)(当然,除非你需要它,例如作为创建一个记录在 SQL 服务器中)。
【解决方案6】:

此答案包含 2 个基于随机和基于名称的 UUID 的生成器,符合 RFC-4122。随意使用和分享。

基于随机的 (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'

基于名称(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

【讨论】:

    【解决方案7】:

    在许多情况下,我们需要对象的全局 UUID,尤其是在事件驱动架构或事件溯源中,我们必须根据日期对事件进行排序,但我们不需要有关时间戳的完整信息。

    我们可以使用ULID 的一种实现,它按字典顺序排序

    格式不同于标准的 UUID,但仍然很简单:

    example value: 01AN4Z07BY79KA1307SR9X4MV3
    
     01AN4Z07BY      79KA1307SR9X4MV3
    
    |----------|    |----------------|
     Timestamp          Randomness
       48bits             80bits
    

    many languages中有实现。

    例如在 Java 中,有一个简单的lib

    代码示例:

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

    【讨论】:

      猜你喜欢
      • 2010-10-08
      • 2010-10-06
      • 1970-01-01
      • 2010-09-11
      • 1970-01-01
      相关资源
      最近更新 更多