如何生成长度为 8 个字符而不是 32 个字符的较短 UUID?

问题描述 投票:0回答:9

UUID 库生成 32 个字符的 UUID。如何生成仅 8 个字符的 UUID?

java uuid
9个回答
94
投票

这是不可能的,因为 UUID 每个定义都是 16 字节的数字。但是,当然,您可以生成 8 个字符长的唯一字符串(请参阅其他答案)。

还要小心生成较长的 UUID 并对它们进行子字符串化,因为 ID 的某些部分可能包含固定字节(例如 MAC、DCE 和 MD5 UUID 就是这种情况)。


91
投票

你可以尝试

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

正如其他人所说,较小 id 发生冲突的概率可能很大。查看生日问题如何适用于您的案例。您可以在这个答案中找到如何计算近似值的很好的解释。


26
投票

第一:即使是 java UUID.randomUUID 或 .net GUID 生成的唯一 ID 也不是 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,那么添加时间戳是个好主意) (顺便说一句:如果您组合(异或)两个随机值,结果总是至少与两者中最随机的值一样随机。


10
投票

正如 @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()), '=');
}

5
投票

这是我在这里使用的类似方法,基于 Anton Purin 的答案生成唯一的错误代码,但依赖于更合适的

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

}

所有有关碰撞的建议仍然适用,请注意。


3
投票

不是 UUID,但这对我有用:

UUID.randomUUID().toString().replace("-","").substring(0,8)

2
投票

这个怎么样?实际上,这段代码最多返回 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
投票

实际上我想要基于时间戳的较短的唯一标识符,因此尝试了以下程序。

可以通过

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,因此我正在考虑两种解决方案,一种使用 DB,另一种不使用 DB。

与数据库

公司名称(简称3个字符)----Random_Number----Key特定的redis COUNTER
(3 个字符) ---------------------------------------------------------- --(2 个字符)----------------(11 个字符)

无数据库

IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ---- 纪元毫秒
(5 个字符) ----------------- (2 个字符) -------------------------------------- (2 个字符) ----------------- (6 个字符)

编码完成后将更新您。


-14
投票

我认为这是不可能的,但你有一个很好的解决方法。

  1. 使用 substring() 剪切 UUID 的末尾
  2. 使用代码
    new Random(System.currentTimeMillis()).nextInt(99999999);
    这将生成最多 8 个字符长的随机 ID。
  3. 生成字母数字 ID:

    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);
    
© www.soinside.com 2019 - 2024. All rights reserved.