生成仅8个字符的UUID


80

UUID库生成32个字符的UUID。

我只想生成8个字符的UUID,可以吗?


当然。但这可能不是那么简单,较短的值实际上不太可能是唯一的。所以为什么?

@delnan,要用于嵌入式环境吗?
阿伦张

1
如果结果字符串可以存储在UTF-8中,则每个字符可能有4个字节。如果可以使用整个范围,则只需要4个UTF-8字符即可表示相同的信息。
EECOLOR

Answers:


72

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

另外,在生成更长的UUID并将其子串化时也要小心,因为ID的某些部分可能包含固定字节(例如,MAC,DCE和MD5 UUID就是这种情况)。



56

您可以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的id碰撞的可能性可能很大。查看生日问题如何适用于您的情况。您可以在此答案中找到很好的解释如何计算近似值。


4
由于org.apache.commons.lang3.RandomStringUtils已弃用,最好org.apache.commons.text.RandomStringGeneratorcommons.apache.org/proper/commons-text中
BrunoJCM

为添加了新的答案RandomStringGenerator,因为它是完全不同的代码。
BrunoJCM

2
对于未来的观看者来说,仅供参考,随机性不能保证唯一性。随机生成器保证随机性;并可以产生一组有效的带有重复值的随机数。
Vishnu Prasad V

RandomStringUtils不被弃用。仅用于简单用途。您可以提供RandomStringUtils不赞成使用的信息来源吗?我可以提供最新版本的文档,RandomStringUtils以证明它不被淘汰:commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…–
krm

仅通过使用已经使用过的uuid检查映射或哈希集,发生碰撞的可能性就很大。
安东

18

第一:即使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,那么最好添加一个时间戳记)(顺便说一句:如果您将两个随机值组合(异或),结果总是至少与最大随机数一样。两者均随机)。


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

这是我在这里使用的类似方法,该方法基于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);
    }

}

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


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,则您可以再次更新答案,但目前它具有误导性。
克朗

添加了一条注释,但是同样,很显然,Apache Commons项目正在将text utils从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个字节。我是否缺少某些东西,因为对我而言这永远无法工作。
Edwin Dalorzo 2014年

2
前8个字节是UUID的最高有效位。根据此答案,低位有效位更随机。这样Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)更好。
DouO 2014年

1

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

通过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。

与DB

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

没有DB

IPADDRESS ---- THREAD_NUMBER ---- INCR_NUMBER ----历时毫秒
(5个字符)-----------------(2个字符)--------- --------------(2个字符)-----------------(6个字符)

编码完成后将更新您。


-11

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

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

14
不幸的是,所有这些方法都可能比您想要的更快地给您重复(即非唯一ID)。
斯蒂芬·C

1
与使用空构造函数相比,使用当前日期进行播种难道不是那么随机吗?
Patrick Favre
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.