Generating 8-character only UUIDs

后端 未结 8 1357
[愿得一人]
[愿得一人] 2020-11-27 03:22

UUID libraries generate 32-character UUIDs.

I want to generate 8-character only UUIDs, is it possible?

相关标签:
8条回答
  • 2020-11-27 03:53

    Actually I want timestamp based shorter unique identifier, hence tried the below program.

    It is guessable with nanosecond + ( endians.length * endians.length ) combinations.

    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.

    with DB

    Company name (shortname 3 chars) ---- Random_Number ---- Key specific redis COUNTER
    (3 char) ------------------------------------------------ (2 char) ---------------- (11 char)

    without DB

    IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- epoch milliseconds
    (5 chars) ----------------- (2char) ----------------------- (2 char) ----------------- (6 char)

    will update you once coding is done.

    0 讨论(0)
  • 2020-11-27 04:00

    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:

    @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.

    0 讨论(0)
  • 2020-11-27 04:02

    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.

    So it is at least a risk based decisions, how long your uuid must be.

    Second: I assume that when you talk about "only 8 characters" you mean a String of 8 normal printable characters.

    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)

    So the way is simple: create a 6 byte random array

     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

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

    0 讨论(0)
  • 2020-11-27 04:02

    As @Cephalopod stated it isn't possible but you can shorten a UUID to 22 characters

    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()), '=');
    }
    
    0 讨论(0)
  • 2020-11-27 04:04

    You can try RandomStringUtils class from 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.

    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 problem applies to your case. You can find nice explanation how to calculate approximation in this answer.

    0 讨论(0)
  • 2020-11-27 04:05

    How about this one? Actually, this code returns 13 characters max, but it shorter than 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 讨论(0)
提交回复
热议问题