【问题标题】:Efficient method to generate UUID String in JAVA (UUID.randomUUID().toString() without the dashes)在 JAVA 中生成 UUID 字符串的有效方法(不带破折号的 UUID.randomUUID().toString())
【发布时间】:2011-04-17 19:17:02
【问题描述】:

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

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

【问题讨论】:

  • 为什么需要删除破折号才能通过 HTTP 传输这样的 UUID?
  • 我认为一般情况下不需要在 HTTP 中删除破折号......哪一点给您带来了麻烦?
  • 也许在移动环境中,如果你还是按传输的每个字节付费,并且使用低带宽和高延迟的网络,在某些场景下节省 4 个字节仍然很重要……
  • 我希望删除破折号,因为我们稍后使用 UUID 字符串作为唯一请求标识符,仅使用十六进制十进制字符比 [a-f0-9-] 更容易。
  • 我删除了 HTTP 部分,因为它不相关(正如 Maxim 解释的那样),只会让读者感到困惑(在 cmets 和答案中都可以看到)。

标签: java random uuid


【解决方案1】:

这样做:

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

【讨论】:

  • 例如,Mongodb 在 ObjectID 中不使用破折号。所以删除破折号对 api 很有用。
  • 我会给你一个理由。我正在使用的 API(高调,众所周知)不允许在其 UUID 中使用破折号。你必须剥离它们。
  • 不需要replaceAll,使用正则表达式。只需执行 .replace("-", "")
  • String类的replace方法有点慢,我觉得
  • @bmscomp 对于第一次调用,它很慢,但是对于下一次调用,没有问题。
【解决方案2】:

如您在此线程的 URL 中所见,不需要从 HTTP 请求中删除破折号。 但是,如果您想准备格式良好的 URL 而不依赖于数据,您应该使用 URLEncoder.encode(String data, String encoding) 而不是更改数据的标准格式。 对于 UUID 字符串表示,破折号是正常的。

【讨论】:

  • "如您在此线程的 URL 中所见,不需要从 HTTP 请求中删除破折号。"不明白,除非 Stack Overflow 之前在其 URL 中使用了 UUID?
  • 不是这个 url 是一个 UUID,而是它有破折号:http://stackoverflow.com/questions/3804591/efficient-method-to-generate-uuid-string-in-java-uuid-randomuuid-tostring-w?rq=1
【解决方案3】:

我使用 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?这意味着你所有的线程都被这个方法阻塞了。
  • 首先,Safehaus 声称 JUG 更快。它可以在您可能不需要的机器上生成唯一的 ID。他们有基于时间的方法,这是所有方法中最重要的一种。是的,这里不需要同步,因为我意识到 SecureRandom 已经是线程安全的。为什么在 SecureRandom 上声明 static final 会减少熵?我很好奇:)这里有更多细节:jug.safehaus.org/FAQ
  • JUG 也可以生成基于随机数的 UUID;但开发人员更喜欢使用基于时间的变体的主要原因是它的速度提高了 10-20 倍(cowtowncoder.com/blog/archives/2010/10/entry_429.html);或者他们不相信随机性会产生唯一的 ID(这有点有趣)
  • jug.safehaus.org 已不存在,但您可以在 raw.github.com/cowtowncoder/java-uuid-generator/3.0/… 找到常见问题解答
  • +1 提到 JUG - 我已经审查了它的用处,但很高兴知道有一些严肃的 java.util.UUID 替代品。
【解决方案4】:

最终基于 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));
}

【讨论】:

  • 不知道为什么这被更多人赞成,这在此处编写的所有选项中以最有效的方法生成了没有“-”的 UUID。字符串替换并不比从长字符串转换更好。确实,两者都是 O(n),但在每分钟生成数百万个 uuid 的规模上,它变得有意义。
【解决方案5】:

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

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

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

【讨论】:

  • 这不可靠。如果前导位为 0,则输出会更短。
  • String.format("0x%016x%016x", f.getMostSignificantBits(), f.getLeastSignificantBits())
  • @galets 虽然我对您解决前导 0 问题的评论投了赞成票,但我想知道与使用 replace 替换破折号的替代方法相比,这是否会更好。
【解决方案6】:

一个简单的解决方案是

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

(和现有的解决方案一样,只是它避免了String#replaceAll调用。这里不需要正则表达式替换,所以String#replace感觉更自然,尽管从技术上讲它仍然是用正则表达式实现的。鉴于生成UUID 比替换成本更高,运行时应该没有显着差异。)

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

使用哪个随机数生成器也是取决于应用程序的权衡。如果它对安全性敏感,通常建议使用 SecureRandom。否则,ThreadLocalRandom 是替代方案(比 SecureRandom 或旧的 Random 更快,但在密码学上不安全)。

【讨论】:

    【解决方案7】:

    我刚刚复制了 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));
    
    }
    

    【讨论】:

      【解决方案8】:

      我使用 org.apache.commons.codec.binary.Base64 将 UUID 转换为长度为 22 个字符且具有与 UUID 相同的唯一性的 url 安全的唯一字符串。

      我在Storing UUID as base64 String上发布了我的代码

      【讨论】:

        【解决方案9】:

        好吧,由于 UUID 在 toString() 上添加连字符(破折号),我们可以从 Java 自己的实现中窃取实现,将字节数组缩短为 32 和调整偏移量。

                public static String special() {
                    UUID uuid = UUID.randomUUID();
                    return fastUUID(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
                }
        
                private static String fastUUID(long lsb, long msb) {
                    byte[] buf = new byte[32];
                    formatUnsignedLong0(lsb,        4, buf, 20, 12);
                    formatUnsignedLong0(lsb >>> 48, 4, buf, 16, 4);
        
                    formatUnsignedLong0(msb,        4, buf, 12, 4);
                    formatUnsignedLong0(msb >>> 16, 4, buf, 8,  4);
                    formatUnsignedLong0(msb >>> 32, 4, buf, 0,  8);
        
                    return new String(buf, 0);
                }
        
                private static final char[] digits = {
                        '0' , '1' , '2' , '3' , '4' , '5' ,
                        '6' , '7' , '8' , '9' , 'a' , 'b' ,
                        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
                        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
                        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
                        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
                };
                private static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) {
                    int charPos = offset + len;
                    int radix = 1 << shift;
                    int mask = radix - 1;
                    do {
                        buf[--charPos] = (byte)digits[((int) val) & mask];
                        val >>>= shift;
                    } while (charPos > offset);
                }
        

        运行它:

                public static void main(String[] args) {
                    IntStream.range(0, 100).forEach(i-> {
                        System.out.println(special());
                    });
                }
        

        生成:

        23f57da8a2784bb5acca553030f82e61
        a14427efd8d147fdae315c1cf20fc53c
        ee972aa1cf85414ca364bef5c74a7e57
        6877ef35eab94b9485c5dd7c8c5a8a56
        578721476629422381c0f625e22564a8
        dbf60f068b5443d7bc6e5280696fed9f
        dd611e870700480d81d394dd2125316c
        04d71b9379ef4db49c28e113485ea76d
        fd4e8cf3f85a45ae8c1b9bfe3e489a4a
        858c4e8297f849b784b65b6096dec4d4
        b30a8ca318a349b486b5693814422555
        351c2fab9bc1426fa3bb512484628f12
        9ce59e01db38405aab82d46f2a236880
        5ffb5acb547a4f15a4621b406391bc0d
        541b5fba8ddb4f1ebbd59cdcd5f59f7c
        77f9460c4baa43a7bbaaf7f2aff205bd
        85fa5254305b4c72b1b7c0103aaee269
        062d45aa86694b06aad841236b839341
        7a265293560f4223ab8248fda502c89b
        b748c13ac45747b99aead4b0a2d7d179
        cbcbf623c75d407fa3c88cfc89a90ed4
        da263eed8771496faebb6290527f77fa
        22231088dec04cffa40fb79ff56c6453
        594a66de4b874b3491649c5d033917f6
        4f6802ebd0cc4a39b25a67191c3af09d
        8878b7ab8aa445cdadbef0f7c70d3deb
        2c3ed0154f0c4ddbad498b7ae928b9bc
        cac1dcaa80e54e2db3248987d2dbda4a
        f9a3567e6dd54bf5900444c8b1c03815
        f0d25d7b615a4495b51c01ab15093a88
        243e45926311437c8b26cede2dc7de25
        e4738c50e4cd448fbac252571c0907df
        261d3593cc054569bcd645885d22c2ee
        64a4796356a04cc4a09506aeb6f5b8fd
        9aeebcbdde074ed69738589ca9bde0f1
        ec040c956861466b84ed7f7cec601be0
        18bd233781e44e7cb152800db4c4edc7
        1b7b251df1244e8db46a45c186aada2b
        3e32f644c9074cb3bbb15c5be1d9b95e
        625309e3ffd14a90bfbd6d48142ac60e
        664f0cf347ce4767add576da584526e7
        fe3893fd376849fe9fed00e328e61470
        254ce1441bbf4a7eae1cdf8d288e61e8
        90896c6b309a49f48cc3b7a1570e1846
        5f47acd1319245648098c1aec9b95f23
        f798033052614b9eae8da7eba4ba3475
        3471c4320e4e431eb1fa9f5eb5cb21e0
        855f473fed034b1fa17f4f65b850e03b
        1245de826d0d4373bdd4cf2157792954
        543a8b16efca4fa2b5263315e8b21660
        2dc186d699274257922853d783c0ec13
        a92e6b1783db4b49a4aaa424b9e1b03f
        16773feb48054cf0942a2a27204b3572
        1e58da2107ac4ee39e28a93b32e1df1d
        67622c19498d4178a1bab6b19087f2c2
        412b6b4474fc43ccbeb1e7707b6420ee
        7d0fd632913c425eb5f087600ccea870
        439687baddb44852a43048b04d38427c
        8b2dcc4e50464429a18b11e4aacf51a2
        2fccb1c832894fe6b0b61bbdf175cd39
        6d224b3d6e8747319fcf01b0309d8a0f
        b4982e3b4b594cb4b334c95c2c96355e
        c47fbaf90d1d4e9caf211f93b742631f
        9440271e8ba6447d9a008e89a93016a6
        8d24274b6a3f436a88362438aa6a221d
        60452bd3f71747ed8c3706abb2235bd5
        6fa93f2ee30740b89496439dd7227a4f
        cc17504cf80641f882c8665ae166ba44
        743efe8eac6e47a789928da4fb5b6f70
        4c4d2df3461448c4a3e934cf4a7ea74f
        b231eb3fb46240d38157764e8906aa7b
        a234ae65f7ed48f6b1887644eed36cc2
        c9cd5ed3df3f4a27957b45498f0c48ef
        3eb2fbbb0a714bb7986aef3ee34f0254
        d15968e605f0440c9e740e3f4e498a9f
        63a8d50e8db24b91a13d4ac2fb6f7d5b
        5377df9296154c57926672ca8b3c9478
        a4db4a3a9d5148648a23aa7f4f77f1e0
        d0aee355a2ba42de89d659385514b0fa
        e92e7702481a4575a66d59c061459c5b
        1b6c542d8f994d85a1312ab2cf4545ce
        88e347a515474ec59013673e5402b97f
        2187d9b2dc2b4d96baceade5ae99db44
        4d641e69ca5b4acf90f8afe238d8a940
        9c0f4c101c434831b928114c5fc0c401
        140e16f6cf134785a98ae9baee5b9e7b
        4dac5910f4d047e1b213c058e2230bf3
        fb50a7e6333f49e4b469234426d5002f
        c96c5f2fa167458eaa6d01997d90a980
        1e79721e587c4a92aa55cdf8195c8c55
        0da27fc5d8384ce299197b4e06cda1d4
        a5e32d9cf5834e86b3fe02bc0e3104d6
        2dc1826647594b1fb728de67d3df363c
        0276371815254198bd22cc76f901b332
        bf9d77b7b4a64e7a97ade2a62af1f8e0
        268cce3249f64895b6b47e86cf296e5b
        d523201fc950435f803bf89d5f042c45
        607a4306b90b467f8b19c2c943bc92ef
        adfa9fb63a874ca1ad746ff573f03f28
        fe88132c70d141e8839ce9e7f0308750
        

        实际上应该与 Java 一样高效(更高效)。

        【讨论】:

          【解决方案10】:

          此实用程序类将 UUID 生成为 带或不带破折号的字符串

          package your.package.name;
          
          import java.security.SecureRandom;
          import java.util.Random;
          
          /**
           * Utility class that creates random-based UUIDs.
           * 
           */
          public abstract class RandomUuidStringCreator {
          
              private static final int RANDOM_VERSION = 4;
          
              /**
               * Returns a random-based UUID as String.
               * 
               * It uses a thread local {@link SecureRandom}.
               * 
               * @return a random-based UUID string
               */
              public static String getRandomUuid() {
                  return getRandomUuid(SecureRandomLazyHolder.SECURE_RANDOM);
              }
          
              /**
               * Returns a random-based UUID as String WITH dashes.
               * 
               * It uses a thread local {@link SecureRandom}.
               * 
               * @return a random-based UUID string
               */
              public static String getRandomUuidWithDashes() {
                  return format(getRandomUuid());
              }
          
              /**
               * Returns a random-based UUID String.
               * 
               * It uses any instance of {@link Random}.
               * 
               * @return a random-based UUID string
               */
              public static String 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
          
                  // Convert MSB and LSB to hexadecimal
                  String msbHex = zerofill(Long.toHexString(msb), 16);
                  String lsbHex = zerofill(Long.toHexString(lsb), 16);
          
                  // Return the UUID
                  return msbHex + lsbHex;
              }
          
              /**
               * Returns a random-based UUID as String WITH dashes.
               * 
               * It uses a thread local {@link SecureRandom}.
               * 
               * @return a random-based UUID string
               */
              public static String getRandomUuidWithDashes(Random random) {
                  return format(getRandomUuid(random));
              }
          
              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;
              }
          
              private static String zerofill(String string, int length) {
                  return new String(lpad(string.toCharArray(), length, '0'));
              }
          
              private static char[] lpad(char[] chars, int length, char fill) {
          
                  int delta = 0;
                  int limit = 0;
          
                  if (length > chars.length) {
                      delta = length - chars.length;
                      limit = length;
                  } else {
                      delta = 0;
                      limit = chars.length;
                  }
          
                  char[] output = new char[chars.length + delta];
                  for (int i = 0; i < limit; i++) {
                      if (i < delta) {
                          output[i] = fill;
                      } else {
                          output[i] = chars[i - delta];
                      }
                  }
                  return output;
              }
          
              private static String format(String string) {
                  char[] input = string.toCharArray();
                  char[] output = new char[36];
          
                  System.arraycopy(input, 0, output, 0, 8);
                  System.arraycopy(input, 8, output, 9, 4);
                  System.arraycopy(input, 12, output, 14, 4);
                  System.arraycopy(input, 16, output, 19, 4);
                  System.arraycopy(input, 20, output, 24, 12);
          
                  output[8] = '-';
                  output[13] = '-';
                  output[18] = '-';
                  output[23] = '-';
          
                  return new String(output);
              }
          
              // Holds lazy secure random
              private static class SecureRandomLazyHolder {
                  static final Random SECURE_RANDOM = new SecureRandom();
              }
          
              /**
               * For tests!
               */
              public static void main(String[] args) {
          
                  System.out.println("// Using `java.security.SecureRandom` (DEFAULT)");
                  System.out.println("RandomUuidCreator.getRandomUuid()");
                  System.out.println();
                  for (int i = 0; i < 5; i++) {
                      System.out.println(RandomUuidStringCreator.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(RandomUuidStringCreator.getRandomUuid(random));
                  }
              }
          }
          
          

          这是输出:

          // Using `java.security.SecureRandom` (DEFAULT)
          RandomUuidStringCreator.getRandomUuid()
          
          'f553ca75657b4b5d85bedf1082785a0b'
          '525ecc389e934f209b97d0f0db09d9c6'
          '93ec6425bb04499ab47b790fd013ab0d'
          'c2d438c620ea4cd5baafd448f9fe945b'
          'fb4bc5734931415e94e78da62cb5fe0d'
          
          // Using `java.util.Random` (FASTER)
          RandomUuidStringCreator.getRandomUuid(new Random())
          
          '051360b5c92d40fbbb89b40842adbacc'
          'a993896538aa43faacbcfd83f913f38b'
          '720684d22c584d5299cb03cdbc1912d2'
          '82cf94ea296a4a138a92825a0068d4a1'
          'a7eda46a215c4e55be3aa957ba74ca9c'
          

          uuid-creator 中有一个编解码器可以更有效地执行此操作:Base16Codec。示例:

          // Returns a base-16 string
          // It is much faster than doing `uuid.toString().replaceAll("-", "")`.
          UuidCodec<String> codec = new Base16Codec();
          String string = codec.encode(UUID.randomUUID());
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-09-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-25
            • 2013-04-17
            相关资源
            最近更新 更多