Генерація 8-символьних UUID


82

Бібліотеки UUID генерують 32-символьні UUID.

Я хочу створити 8-символьні UUID, чи можливо це?


Звичайно. Але це, напевно, не настільки прямолінійно, і коротше дорівнює менше шансів бути справді унікальним. Так чому?

@delnan, для використання у вбудованому середовищі?
Аллен Чжан

1
Якщо отриманий рядок можна зберегти в UTF-8, у вас може бути 4 байти на символ. Якщо ви можете використовувати весь цей діапазон, вам знадобиться лише 4 символи UTF-8 для подання тієї самої інформації.
ЕКОЛОР

Відповіді:


72

Це неможливо, оскільки UUID - це 16-байтове число для визначення. Але, звичайно, ви можете генерувати унікальні рядки довжиною 8 символів (див. Інші відповіді).

Також будьте обережні із створенням довших UUID та їх підрядком, оскільки деякі частини ідентифікатора можуть містити фіксовані байти (наприклад, це стосується MAC, DCE та MD5 UUID).


як щодо позначки часу
Анна Пурані

60

Ви можете спробувати 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); 

Як казали інші, ймовірність зіткнення ІД з меншим ІД може бути значною. Перевірте, як проблема народження стосується вашої справи. У цій відповіді ви знайдете гарне пояснення, як обчислити наближення .


4
Оскільки 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

Тільки перевіряючи карту чи хешсет з уже використаними uuids, ймовірність зіткнення величезна.
Антоне

18

По-перше: Навіть унікальні ідентифікатори, створені 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) два випадкові значення, результат завжди є принаймні таким випадковим, як найбільш випадкові з обох).


7

Як зазначив @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()), '=');
}

2

Це подібний спосіб, який я використовую тут для створення унікального коду помилки, заснованого на відповіді Антона Пуріна, але покладаючись на більш доречний 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/…
krm

Що ж, якщо ви копнете трохи далі, то побачите, що на момент написання цієї відповіді останній випуск справді застарів цей клас: github.com/apache/commons-lang/commits/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
 */
public static String shortUUID() {
  UUID uuid = UUID.randomUUID();
  long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong();
  return Long.toString(l, Character.MAX_RADIX);
}

4
Ви знаєте, що getLong()читається лише перші 8 байт буфера. UUID матиме щонайменше 36 байт. Я чогось пропускаю, бо для мене це ніколи не спрацювало б.
Едвін Далорцо,

2
Перші 8 байт - це найбільш значущі біти UUID. відповідно до цієї відповіді менш значущі біти є більш випадковими. Так Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)краще.
DouO

0

Насправді я хочу коротший унікальний ідентифікатор на основі мітки часу, тому спробував наведену нижче програму.

Це можна вгадати за допомогою 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 символи) ---- Random_Number ---- Ключові слова Redis COUNTER
(3 символи ) -------------------------- ---------------------- (2 символи) ---------------- (11 символів)

без БД

IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- епоха мілісекунд
(5 символів) ----------------- (2char) --------- -------------- (2 символи) ----------------- (6 символів)

оновить вас після завершення кодування.



-11

Я не думаю, що це можливо, але у вас є хороший обхідний шлях.

  1. вирізати кінець вашого UUID за допомогою підрядка ()
  2. використовуйте код, new Random(System.currentTimeMillis()).nextInt(99999999); це створить випадковий ідентифікатор довжиною до 8 символів.
  3. згенерувати буквено-цифровий ідентифікатор:

    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);
    

14
На жаль, всі ці підходи, швидше за все, дадуть вам повторення (тобто не унікальні ідентифікатори) раніше, ніж ви хочете.
Stephen C

1
Хіба посів із поточною датою не є менш випадковим, ніж використання порожнього конструктора?
Патрік Фавр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.