我想要一个有效的实用程序来生成唯一的字节序列。UUID是一个很好的候选,但UUID.randomUUID().toString()会生成像44e128a5-ac7a-4c9a-be4c-224b6bf81b20这样的东西,这很好,但我更喜欢无虚线字符串。
UUID.randomUUID().toString()
44e128a5-ac7a-4c9a-be4c-224b6bf81b20
我正在寻找一种有效的方法来生成随机字符串,只从字母数字字符(没有破折号或任何其他特殊符号)。
这样做:
public static void main(String[] args) { final String uuid = UUID.randomUUID().toString().replace("-", ""); System.out.println("uuid = " + uuid); }
我使用JUG (Java UUID Generator)来生成唯一的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.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)); }
我使用org.apache.commons.codec.binary.Base64将UUID转换为长度为22个字符且具有与UUID相同的唯一性的url安全唯一字符串。
我在存储UUID为base64字符串上发布了我的代码
我很惊讶看到这么多字符串取代UUID的想法。这个怎么样:
UUID temp = UUID.randomUUID(); String uuidString = Long.toHexString(temp.getMostSignificantBits()) + Long.toHexString(temp.getLeastSignificantBits());
这是最快的方法,因为整个UUID的toString()已经更加昂贵,更不用说正则表达式,它必须被解析和执行或替换为空字符串。
一个简单的解决方案是
UUID.randomUUID().toString().replace("-", "")
(像现有的解决方案一样,只是它避免了字符串# replaceAll调用。这里不需要正则表达式替换,因此字符串#替换感觉更自然,尽管从技术上讲它仍然是用正则表达式实现的。由于生成UUID的开销比替换UUID的开销更大,因此在运行时不应该有显著差异。)
对于大多数场景来说,使用UUID类可能已经足够快了,不过我希望一些不需要后期处理的专门手写变体会更快。总之,整个计算的瓶颈通常是随机数生成器。对于UUID类,它使用SecureRandom。
使用哪个随机数生成器也取决于应用程序。如果对安全性敏感,一般建议使用SecureRandom。否则,ThreadLocalRandom是一个替代方案(比securerrandom或旧的随机快,但不加密安全)。
我刚刚复制了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)); }
这个实用工具类生成带或不带破折号的字符串的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。例子:
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());
好吧,由于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一样高效(更高效)。