Создание только 8-значных UUID


82

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

Я хочу сгенерировать только 8-символьные UUID, возможно ли это?


Конечно. Но, вероятно, это не так просто и короче, значит, меньше шансов быть действительно уникальным. Так почему?

@delnan, для использования во встроенной среде?
Allen Zhang

1
Если результирующую строку можно сохранить в UTF-8, у вас потенциально будет 4 байта на символ. Если вы можете использовать весь этот диапазон, вам понадобится всего 4 символа UTF-8 для представления той же информации.
EECOLOR

Ответы:


72

Это невозможно, поскольку UUID - это 16-байтовое число на определение. Но, конечно, вы можете сгенерировать уникальные строки длиной 8 символов (см. Другие ответы).

Также будьте осторожны с генерацией более длинных UUID и их подстрокой, поскольку некоторые части идентификатора могут содержать фиксированные байты (например, это имеет место с UUID MAC, DCE и MD5).


как насчет отметки времени
анна пурани 01

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

Только проверив карту или хэш-набор с уже использованными uuid, вероятность коллизии огромна.
Антон

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/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
 */
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 символа) ---- Случайное_номер ---- Ключевой Redis COUNTER
(3 символа) -------------------------- ---------------------- (2 символа) ---------------- (11 символов)

без БД

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

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



-11

Я не думаю, что это возможно, но у вас есть хорошее решение.

  1. отрежьте конец вашего UUID с помощью substring ()
  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.