Java 生成仅 8 个字符的 UUID
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4267475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generating 8-character only UUIDs
提问by M.J.
UUID libraries generate 32-character UUIDs.
UUID 库生成 32 个字符的 UUID。
I want to generate 8-character only UUIDs, is it possible?
我想生成只有 8 个字符的 UUID,这可能吗?
采纳答案by Cephalopod
It is not possible since a UUID is a 16-byte number per definition.But of course, you can generate 8-character long unique strings (see the other answers).
这是不可能的,因为 UUID 是每个定义的 16 字节数字。但是,当然,您可以生成 8 个字符长的唯一字符串(请参阅其他答案)。
Also be careful with generating longer UUIDs and substring-ing them, since some parts of the ID may contain fixed bytes (e.g. this is the case with MAC, DCE and MD5 UUIDs).
还要小心生成更长的 UUID 并将它们子串化,因为 ID 的某些部分可能包含固定字节(例如,MAC、DCE 和 MD5 UUID 就是这种情况)。
回答by AlexR
I do not think that it is possible but you have a good workaround.
我不认为这是可能的,但你有一个很好的解决方法。
- cut the end of your UUID using substring()
- use code
new Random(System.currentTimeMillis()).nextInt(99999999);
this will generate random ID up to 8 characters long. generate alphanumeric 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);
- 使用 substring() 剪切 UUID 的结尾
- 使用代码
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);
回答by Ralph
First: Even the unique IDs generated by java UUID.randomUUID or .net GUID are not 100% unique. Especialy UUID.randomUUID is "only" a 128 bit (secure) random value. So if you reduce it to 64 bit, 32 bit, 16 bit (or even 1 bit) then it becomes simply less unique.
第一:即使是由 java UUID.randomUUID 或 .net GUID 生成的唯一 ID 也不是 100% 唯一的。特别是 UUID.randomUUID 是“仅”一个 128 位(安全)随机值。因此,如果您将其减少到 64 位、32 位、16 位(甚至 1 位),那么它就变得不那么独特了。
So it is at least a risk based decisions, how long your uuid must be.
所以它至少是一个基于风险的决定,你的 uuid 必须有多长。
Second: I assume that when you talk about "only 8 characters" you mean a String of 8 normal printable characters.
第二:我假设当你谈论“只有 8 个字符”时,你的意思是一个由 8 个普通可打印字符组成的字符串。
If you want a unique string with length 8 printable characters you could use a base64 encoding. This means 6bit per char, so you get 48bit in total (possible not very unique - but maybe it is ok for you application)
如果您想要一个长度为 8 个可打印字符的唯一字符串,您可以使用 base64 编码。这意味着每个字符 6 位,所以你总共得到 48 位(可能不是很独特 - 但也许它适合你的应用程序)
So the way is simple: create a 6 byte random array
所以方法很简单:创建一个6字节的随机数组
SecureRandom rand;
// ...
byte[] randomBytes = new byte[16];
rand.nextBytes(randomBytes);
And then transform it to a Base64 String, for example by org.apache.commons.codec.binary.Base64
然后将其转换为 Base64 字符串,例如通过 org.apache.commons.codec.binary.Base64
BTW: it depends on your application if there is a better way to create "uuid" then by random. (If you create a the UUIDs only once per second, then it is a good idea to add a time stamp) (By the way: if you combine (xor) two random values, the result is always at least as random as the most random of the both).
顺便说一句:这取决于您的应用程序是否有更好的方法来创建“uuid”然后随机创建。(如果您每秒只创建一次 UUID,那么添加时间戳是个好主意)(顺便说一句:如果您组合(异或)两个随机值,结果总是至少与大多数随机值一样随机两者都是随机的)。
回答by sanghoon2
How about this one? Actually, this code returns 13 characters max, but it shorter than UUID.
这个怎么样?实际上,此代码最多返回 13 个字符,但比 UUID 短。
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);
}
回答by bstick12
As @Cephalopod stated it isn't possible but you can shorten a UUID to 22 characters
正如@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()), '=');
}
回答by Anton Purin
You can try RandomStringUtils
class from apache.commons:
您可以RandomStringUtils
从 apache.commons尝试课程:
import org.apache.commons.lang3.RandomStringUtils;
final int SHORT_ID_LENGTH = 8;
// all possible unicode characters
String shortId = RandomStringUtils.random(SHORT_ID_LENGTH);
Please keep in mind, that it will contain all possible characters which is neither URL nor human friendly.
请记住,它将包含所有可能的字符,这些字符既不是 URL 也不是人类友好的。
So check out other methods too:
所以也看看其他方法:
// 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);
As others said probability of id collision with smaller id can be significant. Check out how birthday problemapplies to your case. You can find nice explanation how to calculate approximation in this answer.
正如其他人所说,id 与较小 id 冲突的可能性可能很大。查看生日问题如何适用于您的情况。您可以在此答案中找到如何计算近似值的很好解释。
回答by Kanagavelu Sugumar
Actually I want timestamp based shorter unique identifier, hence tried the below program.
实际上我想要基于时间戳的更短的唯一标识符,因此尝试了以下程序。
It is guessable with nanosecond + ( endians.length * endians.length )
combinations.
可以用nanosecond + ( 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();
}
}
UPDATE: This code will work on single JVM, but we should think on distributed JVM, hence i am thinking two solutions one with DB and another one without DB.
更新:此代码将在单个 JVM 上运行,但我们应该考虑分布式 JVM,因此我在考虑两种解决方案,一种带 DB,另一种不带 DB。
with DB
带数据库
Company name (shortname 3 chars) ---- Random_Number ---- Key specific redis COUNTER
(3 char) ------------------------------------------------ (2 char) ---------------- (11 char)
公司名称 (shortname 3 chars) ---- Random_Number ---- Key 特定的 redis COUNTER
(3 chars ) -------------------------- ---------------------- (2 个字符) ---------------- (11 个字符)
without DB
没有数据库
IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- epoch milliseconds
(5 chars) ----------------- (2char) ----------------------- (2 char) ----------------- (6 char)
IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- 纪元毫秒
(5 个字符)-----------------(2 个字符)--------- -------------- (2 个字符) ----------------- (6 个字符)
will update you once coding is done.
编码完成后将更新您。
回答by BrunoJCM
This is a similar way I'm using here to generate an unique error code, based on Anton Purin answer, but relying on the more appropriate org.apache.commons.text.RandomStringGenerator
instead of the (once, not anymore) deprecated org.apache.commons.lang3.RandomStringUtils
:
这是我在这里使用的一种类似的方式来生成一个唯一的错误代码,基于 Anton Purin 的答案,但依赖更合适的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);
}
}
All advices about collision still apply, please be aware of them.
所有关于碰撞的建议仍然适用,请注意。