在JAVA中生成UUID字符串的有效方法(不带破折号的UUID.randomUUID()。toString())


154

我想要一个高效的实用程序来生成唯一的字节序列。UUID是一个很好的候选人,但会UUID.randomUUID().toString()生成类似的东西44e128a5-ac7a-4c9a-be4c-224b6bf81b20,但我更喜欢无破折号的字符串。

我正在寻找一种仅从字母数字字符(无破折号或任何其他特殊符号)生成随机字符串的有效方法。


38
为什么要删除这样的UUID以便通过HTTP传输的破折号?
布鲁诺

6
我认为一般不需要在HTTP中删除破折号...哪一点引起了您的麻烦?
乔恩·斯基特

2
也许在移动环境中,如果您仍然为传输的每个字节付费,并使用低带宽和高延迟网络,则在某些情况下保存4个字节仍然很重要……
Guido 2010年

2
我希望删除破折号,因为稍后我们将UUID字符串用作唯一的请求标识符,与[a-f0-9-]相比,仅使用十六进制小数字符要容易得多。
Maxim Veksler 2010年

我删除了HTTP部分,因为它不相关(如Maxim解释),仅使读者感到困惑(在注释和答案中都可以看到)。
Ondra参观Žižka

Answers:


274

这样做:

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

例如,Mongodb在ObjectID中不使用破折号。因此,删除破折号对于api可能很有用。
Alexey Ryazhskikh

1
我给你一个理由。我正在使用的API(知名度很高)不允许在UUID中使用破折号。你必须脱掉它们。
迈克尔·盖恩斯

19
无需执行使用正则表达式的replaceAll。只做.replace(“-”,“”)
Craigo

1
我认为String类的replace方法有点慢,我认为
bmscomp

@bmscomp第一次调用很慢,但是对于下一次调用没有问题。
gaurav

30

正如您在该线程的URL中看到的那样,不需要从HTTP请求中删除短划线。但是,如果您要准备格式正确的URL,而不依赖数据,则应使用URLEncoder.encode(String data,String encoding)而不是更改数据的标准格式。对于UUID字符串,破折号是正常的。


“不需要从HTTP请求中删除破折号,因为您可以在此线程的URL中看到。” 除非Stack Overflow以前在其URL中使用过UUID,否则不理解吗?
RenniePet 2014年

1
并非该网址是UUID,而是带有破折号:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
Octavia Togami,2014年

12

最终基于UUID.java实现编写了自己的东西。请注意,我并不是在生成UUID,而是以我能想到的最有效的方式生成一个随机的32字节十六进制字符串。

实作

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

public class RandomUtil {
    // Maxim: Copied from UUID implementation :)
    private static volatile SecureRandom numberGenerator = null;
    private static final long MSB = 0x8000000000000000L;

    public static String unique() {
        SecureRandom ng = numberGenerator;
        if (ng == null) {
            numberGenerator = ng = new SecureRandom();
        }

        return Long.toHexString(MSB | ng.nextLong()) + Long.toHexString(MSB | ng.nextLong());
    }       
}

用法

RandomUtil.unique()

测验

我测试了一些输入以确保其正常工作:

public static void main(String[] args) {
    System.out.println(UUID.randomUUID().toString());
    System.out.println(RandomUtil.unique());

    System.out.println();
    System.out.println(Long.toHexString(0x8000000000000000L |21));
    System.out.println(Long.toBinaryString(0x8000000000000000L |21));
    System.out.println(Long.toHexString(Long.MAX_VALUE + 1));
}

1
不知道为什么还要增加这个价格,在这里编写的所有选项中,这是最有效的方法,它生成的UUID不带“-”。字符串替换不是更好,然后从long转换为string。两者都为O(n)是正确的,但是在规模上,每分钟您产生数百万个uuid的值就变得有意义。
Maxim Veksler '16

10

我使用JUG(Java UUID生成器)生成唯一ID。它在JVM中是唯一的。很好用。这是供您参考的代码:

private static final SecureRandom secureRandom = new SecureRandom();
private static final UUIDGenerator generator = UUIDGenerator.getInstance();

public synchronized static String generateUniqueId() {
  UUID uuid = generator.generateRandomBasedUUID(secureRandom);

  return uuid.toString().replaceAll("-", "").toUpperCase();
}

您可以从以下网址下载该库:https : //github.com/cowtowncoder/java-uuid-generator


对于您的情况,UUID.randomUUID()。toString()有什么问题?还要注意,您(理论上)通过持有静态最终的SecureRandom(使其变得易变)来降低熵。还为什么要同步generateUniqueId?这意味着您的所有线程都被此方法阻止。
Maxim Veksler 2010年

首先,Safehaus声称JUG速度更快。而且它可以在您不需要的机器之间生成唯一的ID。他们有基于时间的方法,这是所有方法中最差的一种。是的,这里不需要同步,因为我意识到SecureRandom已经是线程安全的。为什么在SecureRandom上声明static final会减少熵?我很好奇:)这里有更多详细信息:jug.safehaus.org/FAQ
Sheng Chien 2010年

JUG也可以生成基于随机数的UUID。但是开发人员喜欢使用基于时间的变体的主要原因是,它的速度提高了10到20倍(cowtowncoder.com/blog/archives/2010/10/entry_429.html);还是他们不相信随机性会产生唯一的ID(这很有趣)
StaxMan 2010年

jug.safehaus.org不再存在,但是您可以在raw.github.com/cowtowncoder/java-uuid-generator/3.0/…上
Daniel Serodio 2012年

+1提及JUG-我已经审查了它的用处,但很高兴知道还有一些严肃的java.util.UUID选择。
格雷格·杜比奇

8

一个简单的解决方案是

UUID.randomUUID().toString().replace("-", "")

(与现有解决方案一样,只是避免了String#replaceAll调用。此处不需要进行正则表达式替换,因此String#replace感觉更自然,尽管从技术上讲,它仍是使用正则表达式实现的。鉴于UUID的生成是比替换产品更昂贵,运行时间不应有明显差异。)

在大多数情况下,使用UUID类可能足够快,尽管我希望某些不需要后期处理的专用手写变体更快。无论如何,总体计算的瓶颈通常是随机数生成器。对于UUID类,它使用SecureRandom

使用哪个随机数发生器也是一个折衷方案,具体取决于应用程序。如果对安全性敏感,通常建议使用SecureRandom。否则,可以选择ThreadLocalRandom(比SecureRandom或旧的Random快,但不是加密安全的)。


7

我很惊讶地看到如此多的UUID字符串替换想法。这个怎么样:

UUID temp = UUID.randomUUID();
String uuidString = Long.toHexString(temp.getMostSignificantBits())
     + Long.toHexString(temp.getLeastSignificantBits());

这是执行此操作的快速方法,因为整个UUID的toString()已经更昂贵了,更不用说必须解析和执行的正则表达式,或者用空字符串替换。


6
这是不可靠的。输出将是短的如果前导比特是0。
OG多德

7
String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
galets 2016年

@galets尽管我对使用0开头的问题解决问题的投票表示赞成,但我想知道与使用代替破折号的替代方法相比,这是否会更好replace
igorcadelima


3

我刚刚复制了UUID toString()方法,并对其进行了更新,以从其中删除“-”。这将比任何其他解决方案都更快,更直接

public String generateUUIDString(UUID uuid) {
    return (digits(uuid.getMostSignificantBits() >> 32, 8) +
            digits(uuid.getMostSignificantBits() >> 16, 4) +
            digits(uuid.getMostSignificantBits(), 4) +
            digits(uuid.getLeastSignificantBits() >> 48, 4) +
            digits(uuid.getLeastSignificantBits(), 12));
}

/** Returns val represented by the specified number of hex digits. */
private String digits(long val, int digits) {
    long hi = 1L << (digits * 4);
    return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}

用法:

generateUUIDString(UUID.randomUUID())

使用反射的另一种实现

public String generateString(UUID uuid) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

    if (uuid == null) {
        return "";
    }

    Method digits = UUID.class.getDeclaredMethod("digits", long.class, int.class);
    digits.setAccessible(true);

    return ( (String) digits.invoke(uuid, uuid.getMostSignificantBits() >> 32, 8) +
            digits.invoke(uuid, uuid.getMostSignificantBits() >> 16, 4) +
            digits.invoke(uuid, uuid.getMostSignificantBits(), 4) +
            digits.invoke(uuid, uuid.getLeastSignificantBits() >> 48, 4) +
            digits.invoke(uuid, uuid.getLeastSignificantBits(), 12));

}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.