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

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

61 голосов
/ 24 ноября 2010

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

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

Ответы [ 8 ]

53 голосов
/ 24 ноября 2010

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

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

28 голосов
/ 12 июля 2017

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

Как уже говорили другие, вероятность столкновения идентификатора с меньшим идентификатором может быть значительной. Проверьте, как проблема дня рождения относится к вашему делу. Вы можете найти хорошее объяснение, как рассчитать аппроксимацию в этом ответе .

15 голосов
/ 24 ноября 2010

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

5 голосов
/ 15 сентября 2016

Как сказал @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()), '=');
}
3 голосов
/ 10 декабря 2017

Это аналогичный способ, который я использую здесь, чтобы сгенерировать уникальный код ошибки, основываясь на ответе Антона Пурина, но полагаясь на 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);
    }

}

Все советы о столкновении все еще применяются, пожалуйста, имейте в виду.

1 голос
/ 06 сентября 2017

На самом деле мне нужен более короткий уникальный идентификатор на основе метки времени, поэтому я попробовал следующую программу.

Это возможно с 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, поэтому я думаю о двух решениях, одно с БД, а другое без БД.

с БД

Название компании (сокращенное название 3chars) ---- Random_Number ---- ключ, специфичный для redis COUNTER
(3 символа) ------------------------------------------------ (2 символа) ---------------- (11 символов)

без базы данных

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

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

0 голосов
/ 19 июня 2013

Как насчет этого? На самом деле этот код возвращает не более 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);
}
0 голосов
/ 24 ноября 2010

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

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