UUID库生成32个字符的UUID。
我希望能够生成只有8个字符的UUID,这是否可能?
根据定义,UUID是一个16字节的数字,因此不可能生成8个字符长的唯一字符串。但是当然,您可以生成8个字符长的唯一字符串(请参见其他答案)。
另外,在生成更长的UUID并将其子串化时要小心,因为ID的某些部分可能包含固定字节(例如MAC、DCE和MD5 UUID就是这种情况)。
RandomStringUtils
类:import org.apache.commons.lang3.RandomStringUtils;
final int SHORT_ID_LENGTH = 8;
// all possible unicode characters
String shortId = RandomStringUtils.random(SHORT_ID_LENGTH);
请记住,这将包含所有可能的字符,既不是URL也不是人类友好的。
因此,请查看其他方法:
// HEX: 0-9, a-f. For example: 6587fddb, c0f182c1
shortId = RandomStringUtils.random(8, "0123456789abcdef");
// a-z, A-Z. For example: eRkgbzeF, MFcWSksx
shortId = RandomStringUtils.randomAlphabetic(8);
// 0-9. For example: 76091014, 03771122
shortId = RandomStringUtils.randomNumeric(8);
// a-z, A-Z, 0-9. For example: WRMcpIk7, s57JwCVA
shortId = RandomStringUtils.randomAlphanumeric(8);
如其他人所说,较小的id发生碰撞的概率可能相当大。查看 生日问题 如何应用到您的情况中。您可以在此答案中找到很好的解释如何计算近似值。
org.apache.commons.lang3.RandomStringUtils
已被弃用,建议使用位于https://commons.apache.org/proper/commons-text/的`org.apache.commons.text.RandomStringGenerator`。请注意,翻译后的内容与原文意思相同但更易懂,并且没有额外的解释或信息。 - Bruno MedeirosRandomStringGenerator
添加了一个新的答案,因为代码有很大不同。 - Bruno MedeirosRandomStringUtils
没有被弃用。它旨在用于简单的使用。您能提供 RandomStringUtils
被弃用的信息来源吗?我可以提供最新版本的 RandomStringUtils
文档作为证明,表明它没有被弃用:http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/RandomStringUtils.html - krm首先,即使是由Java UUID.randomUUID或.NET GUID生成的唯一ID也不是100%唯一的。尤其是UUID.randomUUID仅是一个128位(安全)随机值。因此,如果将其减少到64位、32位、16位(甚至1位),则它就变得不那么唯一了。
因此,至少在一定程度上,决定你的UUID长度的风险是基于决策的。
其次,我假设当你谈论“仅有8个字符”时,你指的是一个由8个普通可打印字符组成的字符串。
如果您想要一个具有长度为8个可打印字符的唯一字符串,则可以使用Base64编码。这意味着每个字符6位,因此您总共获得48位(可能并不是很唯一-但也许对于您的应用程序来说这没问题)
因此,方法很简单:创建一个6字节的随机数组
SecureRandom rand;
// ...
byte[] randomBytes = new byte[16];
rand.nextBytes(randomBytes);
然后将其转换为Base64字符串,例如使用org.apache.commons.codec.binary.Base64
顺便说一句:如果有更好的方法创建“uuid”取决于您的应用程序。(如果您每秒仅创建一次UUID,则添加时间戳是个不错的选择) (顺便说一下:如果您结合(异或)两个随机值,结果始终至少与两者中最随机的相同)。
正如@Cephalopod所说,这是不可能的,但你可以将UUID缩短到22个字符。
public static String encodeUUIDBase64(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return StringUtils.trimTrailingCharacter(BaseEncoding.base64Url().encode(bb.array()), '=');
}
org.apache.commons.text.RandomStringGenerator
,而不是(曾经,现在已经不再)弃用的 org.apache.commons.lang3.RandomStringUtils
。@Singleton
@Component
public class ErrorCodeGenerator implements Supplier<String> {
private RandomStringGenerator errorCodeGenerator;
public ErrorCodeGenerator() {
errorCodeGenerator = new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(t -> t >= '0' && t <= '9', t -> t >= 'A' && t <= 'Z', t -> t >= 'a' && t <= 'z')
.build();
}
@Override
public String get() {
return errorCodeGenerator.generate(8);
}
}
所有有关碰撞的建议仍然适用,请注意它们。
RandomStringUtils
没有被弃用。它旨在用于简单的使用。您能提供 RandomStringUtils
被弃用的信息来源吗?我可以提供最新版本的 RandomStringUtils
文档作为证明,表明它没有被弃用:http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/RandomStringUtils.html - krmRandomStringUtils
没有被弃用,根据您提供的参考资料,保持不弃用是有充分理由的,因为对于简单的用例,它比RandomStringGenerator
更简单易用。也许您可以更新您的答案?如果/当RandomStringUtils
或其简单用例的功能被移动到commons.text
中,那么您可以再次更新您的答案,但目前这会引起误导。 - krmcommons.lang
移动到 commons.text
,除非已经在其他地方使用它,否则没有理由使用前者而不是后者。这里的简单性相当主观,我认为我的答案仍然非常简单,我永远不会改变它以需要导入 Commons Lang 的东西。 - Bruno Medeiros虽不是 UUID,但对我而言可行:
UUID.randomUUID().toString().replace("-","").substring(0,8)
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* Generate short UUID (13 characters)
*
* @return short UUID
*/
public static String shortUUID() {
UUID uuid = UUID.randomUUID();
long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong();
return Long.toString(l, Character.MAX_RADIX);
}
getLong()
只会读取缓冲区的前8个字节。UUID至少有36个字节。如果我没有漏掉什么,对我来说这永远不会起作用。 - Edwin DalorzoLong.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)
更好。 - DouO实际上我想要基于时间戳的短唯一标识符,因此尝试了下面的程序。
它可以通过 纳秒 + ( endians.length * endians.length )
的组合进行猜测。
public class TimStampShorterUUID {
private static final Character [] endians =
{'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',
'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',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
private static ThreadLocal<Character> threadLocal = new ThreadLocal<Character>();
private static AtomicLong iterator = new AtomicLong(-1);
public static String generateShorterTxnId() {
// Keep this as secure random when we want more secure, in distributed systems
int firstLetter = ThreadLocalRandom.current().nextInt(0, (endians.length));
//Sometimes your randomness and timestamp will be same value,
//when multiple threads are trying at the same nano second
//time hence to differentiate it, utilize the threads requesting
//for this value, the possible unique thread numbers == endians.length
Character secondLetter = threadLocal.get();
if (secondLetter == null) {
synchronized (threadLocal) {
if (secondLetter == null) {
threadLocal.set(endians[(int) (iterator.incrementAndGet() % endians.length)]);
}
}
secondLetter = threadLocal.get();
}
return "" + endians[firstLetter] + secondLetter + System.nanoTime();
}
public static void main(String[] args) {
Map<String, String> uniqueKeysTestMap = new ConcurrentHashMap<>();
Thread t1 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t3 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t4 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t5 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t6 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t7 = new Thread() {
@Override
public void run() {
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
}
}
更新:这段代码可以在单个JVM上工作,但我们应该考虑分布式JVM,因此我正在考虑两种解决方案,一种是使用数据库,另一种是不使用数据库。
使用数据库
公司名称(缩写3个字符)---- 随机数 ---- 特定密钥的redis计数器
(3个字符) --------------------------------------------------(2个字符) -----------------(11个字符)
不使用数据库
IP地址 ---- 线程数 ---- 增量数 ---- 毫秒级时间戳
(5个字符) ------------------(2个字符) ------------------------(2个字符) ------------------(6个字符)
完成编码后会通知您更新。
我不认为这是可能的,但你有一个很好的解决方法。
new Random(System.currentTimeMillis()).nextInt(99999999);
生成最多8个字符长的随机ID。生成字母数字ID:
char[] chars = "abcdefghijklmnopqrstuvwxyzABSDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
Random r = new Random(System.currentTimeMillis());
char[] id = new char[8];
for (int i = 0; i < 8; i++) {
id[i] = chars[r.nextInt(chars.length)];
}
return new String(id);