Конечно. Но, вероятно, это не так просто и короче, значит, меньше шансов быть действительно уникальным. Так почему?
@delnan, для использования во встроенной среде?
Allen Zhang
1
Если результирующую строку можно сохранить в UTF-8, у вас потенциально будет 4 байта на символ. Если вы можете использовать весь этот диапазон, вам понадобится всего 4 символа UTF-8 для представления той же информации.
EECOLOR
Ответы:
72
Это невозможно, поскольку UUID - это 16-байтовое число на определение. Но, конечно, вы можете сгенерировать уникальные строки длиной 8 символов (см. Другие ответы).
Также будьте осторожны с генерацией более длинных UUID и их подстрокой, поскольку некоторые части идентификатора могут содержать фиксированные байты (например, это имеет место с UUID MAC, DCE и MD5).
Как говорили другие, вероятность столкновения идентификатора с меньшим идентификатором может быть значительной. Посмотрите, как проблема с днем рождения применима к вашему случаю. В этом ответе вы можете найти хорошее объяснение, как рассчитать приближение .
Поскольку org.apache.commons.lang3.RandomStringUtilsон устарел, вам лучше использовать org.apache.commons.text.RandomStringGeneratorв commons.apache.org/proper/commons-text
BrunoJCM
Добавлен новый ответ для RandomStringGenerator, поскольку это совсем другой код.
BrunoJCM
2
Просто к сведению будущих зрителей, случайность не гарантирует уникальности. Генераторы случайных чисел гарантируют случайность; и может производить действительный набор случайных чисел с повторяющимися значениями.
Вишну Прасад V
RandomStringUtilsНЕ устарел. Он предназначен для простого использования. Можете ли вы предоставить источник информации, которая RandomStringUtilsустарела? Я могу предоставить документацию по последней версии RandomStringUtilsв качестве доказательства того, что она не устарела: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm
Только проверив карту или хэш-набор с уже использованными uuid, вероятность коллизии огромна.
Антон
18
Во-первых: даже уникальные идентификаторы, сгенерированные java UUID.randomUUID или .net GUID, не уникальны на 100%. В частности, UUID.randomUUID - это «всего лишь» 128-битное (безопасное) случайное значение. Поэтому, если вы уменьшите его до 64-битного, 32-битного, 16-битного (или даже 1-битного), он станет просто менее уникальным.
Так что, по крайней мере, решение о том, какой длины должен быть ваш uuid, зависит от риска.
Во-вторых: я предполагаю, что когда вы говорите «всего 8 символов», вы имеете в виду строку из 8 обычных печатных символов.
Если вам нужна уникальная строка длиной 8 печатных символов, вы можете использовать кодировку base64. Это означает 6 бит на символ, поэтому вы получите всего 48 бит (возможно, не очень уникально, но, возможно, это нормально для вашего приложения)
Итак, способ прост: создать 6-байтовый случайный массив
А затем преобразовать его в строку Base64, например, org.apache.commons.codec.binary.Base64
Кстати: это зависит от вашего приложения, есть ли лучший способ создать "uuid", чем случайным образом. (Если вы создаете UUID только один раз в секунду, тогда рекомендуется добавить отметку времени) (Кстати: если вы объедините (xor) два случайных значения, результат всегда будет как минимум таким же случайным, как и большинство случайный из обоих).
Это аналогичный способ, который я использую здесь для генерации уникального кода ошибки на основе ответа Антона Пурина, но полагаюсь на более подходящий org.apache.commons.text.RandomStringGeneratorвместо устаревшего (один раз, а не больше) org.apache.commons.lang3.RandomStringUtils:
@Singleton@ComponentpublicclassErrorCodeGeneratorimplementsSupplier<String> {
private RandomStringGenerator errorCodeGenerator;
publicErrorCodeGenerator(){
errorCodeGenerator = new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(t -> t >= '0' && t <= '9', t -> t >= 'A' && t <= 'Z', t -> t >= 'a' && t <= 'z')
.build();
}
@Overridepublic String get(){
return errorCodeGenerator.generate(8);
}
}
Все советы о столкновении по-прежнему актуальны, имейте в виду.
RandomStringUtilsНЕ устарел. Он предназначен для простого использования. Можете ли вы предоставить источник информации, которая RandomStringUtilsустарела? Я могу предоставить документацию по последней версии RandomStringUtilsв качестве доказательства того, что она не устарела: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm
Что ж, если вы копнете немного дальше, вы увидите, что на момент написания этого ответа последний выпуск действительно устарел этот класс: github.com/apache/commons-lang/commit/master/src / main / java / org /… Вероятно, некоторые отзывы ( user.commons.apache.narkive.com/GVBG2Ar0/… ) вернулись. Вы не должны использовать что-либо, commons.langчто не имеет прямого отношения к самому языку, в любом случае commons.textбыло создано с определенной целью.
BrunoJCM
Спасибо за объяснение BrunoJCM. В настоящее время RandomStringUtilsне является устаревшим, и, согласно предоставленным вами ссылкам, есть веская причина оставить его не устаревшим, потому что его гораздо проще использовать, чем RandomStringGeneratorдля простых случаев использования. Может можно обновить свой ответ? Если / когда RandomStringUtilsили его функциональность для простых вариантов использования будет перенесена в commons.text, тогда вы можете обновить свой ответ снова, но в настоящее время это вводит в заблуждение.
krm
Добавлено примечание, но опять же, ясно, что проект Apache Commons перемещает текстовые утилиты из commons.langв commons.text, нет никаких причин для использования первого, а не второго, кроме того, что уже использует его где-то еще. Простота здесь довольно субъективна, я считаю, что мой ответ по-прежнему очень прост, и я бы никогда не изменил его на что-то, что потребует импорта Commons Lang.
BrunoJCM
1
Как насчет этого? Фактически, этот код возвращает максимум 13 символов, но он короче, чем UUID.
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* Generate short UUID (13 characters)
*
* @return short UUID
*/publicstatic 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 байтов. Я что-то упускаю, потому что для меня это никогда не сработает.
Эдвин Далорцо
2
Первые 8 байтов - это самые старшие биты UUID. согласно этому ответу менее значимые биты более случайны. Так Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)лучше.
DouO
0
На самом деле мне нужен более короткий уникальный идентификатор на основе временной метки, поэтому я попробовал программу ниже.
Это можно угадать nanosecond + ( endians.length * endians.length )комбинациями.
publicclassTimStampShorterUUID{
privatestaticfinal 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'
};
privatestatic ThreadLocal<Character> threadLocal = new ThreadLocal<Character>();
privatestatic AtomicLong iterator = new AtomicLong(-1);
publicstatic String generateShorterTxnId(){
// Keep this as secure random when we want more secure, in distributed systemsint 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();
}
publicstaticvoidmain(String[] args){
Map<String, String> uniqueKeysTestMap = new ConcurrentHashMap<>();
Thread t1 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t2 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t3 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t4 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t5 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t6 = new Thread() {
@Overridepublicvoidrun(){
while(true) {
String time = generateShorterTxnId();
String result = uniqueKeysTestMap.put(time, "");
if(result != null) {
System.out.println("failed! - " + time);
}
}
}
};
Thread t7 = new Thread() {
@Overridepublicvoidrun(){
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 COUNTER
(3 символа) -------------------------- ---------------------- (2 символа) ---------------- (11 символов)
Я не думаю, что это возможно, но у вас есть хорошее решение.
отрежьте конец вашего UUID с помощью substring ()
используйте код, new Random(System.currentTimeMillis()).nextInt(99999999);
это сгенерирует случайный идентификатор длиной до 8 символов.
создать буквенно-цифровой идентификатор:
char[] chars = "abcdefghijklmnopqrstuvwxyzABSDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
Random r = new Random(System.currentTimeMillis());
char[] id = newchar[8];
for (int i = 0; i < 8; i++) {
id[i] = chars[r.nextInt(chars.length)];
}
returnnew String(id);
Ответы:
Это невозможно, поскольку UUID - это 16-байтовое число на определение. Но, конечно, вы можете сгенерировать уникальные строки длиной 8 символов (см. Другие ответы).
Также будьте осторожны с генерацией более длинных UUID и их подстрокой, поскольку некоторые части идентификатора могут содержать фиксированные байты (например, это имеет место с UUID MAC, DCE и MD5).
источник
Вы можете попробовать
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);
Имейте в виду, что он будет содержать все возможные символы, которые не подходят ни для 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);
Как говорили другие, вероятность столкновения идентификатора с меньшим идентификатором может быть значительной. Посмотрите, как проблема с днем рождения применима к вашему случаю. В этом ответе вы можете найти хорошее объяснение, как рассчитать приближение .
источник
org.apache.commons.lang3.RandomStringUtils
он устарел, вам лучше использоватьorg.apache.commons.text.RandomStringGenerator
в commons.apache.org/proper/commons-textRandomStringGenerator
, поскольку это совсем другой код.RandomStringUtils
НЕ устарел. Он предназначен для простого использования. Можете ли вы предоставить источник информации, котораяRandomStringUtils
устарела? Я могу предоставить документацию по последней версииRandomStringUtils
в качестве доказательства того, что она не устарела: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…Во-первых: даже уникальные идентификаторы, сгенерированные java UUID.randomUUID или .net GUID, не уникальны на 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 только один раз в секунду, тогда рекомендуется добавить отметку времени) (Кстати: если вы объедините (xor) два случайных значения, результат всегда будет как минимум таким же случайным, как и большинство случайный из обоих).
источник
Как заявил @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
в качестве доказательства того, что она не устарела: commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…commons.lang
что не имеет прямого отношения к самому языку, в любом случаеcommons.text
было создано с определенной целью.RandomStringUtils
не является устаревшим, и, согласно предоставленным вами ссылкам, есть веская причина оставить его не устаревшим, потому что его гораздо проще использовать, чемRandomStringGenerator
для простых случаев использования. Может можно обновить свой ответ? Если / когдаRandomStringUtils
или его функциональность для простых вариантов использования будет перенесена вcommons.text
, тогда вы можете обновить свой ответ снова, но в настоящее время это вводит в заблуждение.commons.lang
вcommons.text
, нет никаких причин для использования первого, а не второго, кроме того, что уже использует его где-то еще. Простота здесь довольно субъективна, я считаю, что мой ответ по-прежнему очень прост, и я бы никогда не изменил его на что-то, что потребует импорта Commons Lang.Как насчет этого? Фактически, этот код возвращает максимум 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); }
источник
getLong()
читаются только первые 8 байтов буфера. UUID будет иметь не менее 36 байтов. Я что-то упускаю, потому что для меня это никогда не сработает.Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)
лучше.На самом деле мне нужен более короткий уникальный идентификатор на основе временной метки, поэтому я попробовал программу ниже.
Это можно угадать
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(); } }
ОБНОВЛЕНИЕ : этот код будет работать на одной JVM, но мы должны думать о распределенной JVM, поэтому я думаю о двух решениях: одно с БД, а другое без БД.
с БД
Название компании (короткое имя 3 символа) ---- Случайное_номер ---- Ключевой Redis COUNTER
(3 символа) -------------------------- ---------------------- (2 символа) ---------------- (11 символов)
без БД
IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- эпоха в миллисекундах
(5 символов) ----------------- (2char) --------- -------------- (2 символа) ----------------- (6 символов)
обновит вас после завершения кодирования.
источник
Не UUID, но у меня это работает:
UUID.randomUUID().toString().replace("-","").substring(0,8)
источник
Я не думаю, что это возможно, но у вас есть хорошее решение.
new Random(System.currentTimeMillis()).nextInt(99999999);
это сгенерирует случайный идентификатор длиной до 8 символов.создать буквенно-цифровой идентификатор:
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);
источник