在java中生成唯一的值会导致重复

在java中生成唯一的值会导致重复,java,Java,我有一个在java中生成唯一ID的逻辑,如下所示 private static String generateUniqueNum() { final int LENGTH = 20; final long uniqueNumber = abs(Long.valueOf(UUID.randomUUID().hashCode())) + System.currentTimeMillis() + abs(random.nextLong());

我有一个在java中生成唯一ID的逻辑,如下所示

private static String generateUniqueNum()
    {
        final int LENGTH = 20;

        final long uniqueNumber = abs(Long.valueOf(UUID.randomUUID().hashCode())) + System.currentTimeMillis() + abs(random.nextLong());

        String value = Long.toString(uniqueNumber).replaceAll("-", "");



        final int endIndex = value.length() <= LENGTH ? value.length() : LENGTH;
        return String.format("MN%s", value.substring(0, endIndex));
    }

     private static Long abs(Long number){
            if(null == number || 0 < number){
                return 0L;
            }
            return Math.abs(number);
        }
private静态字符串generateUniqueNum()
{
最终整数长度=20;
final long uniqueNumber=abs(long.valueOf(UUID.randomuid().hashCode())+System.currentTimeMillis()+abs(random.nextLong());
字符串值=Long.toString(uniqueNumber.replaceAll(“-”,”);

final int endIndex=value.length()UUID是唯一的,但使用128位(两个长)

最好使用数据库键,例如字符串表示法。对于不太安全的

return uuid.getLeastSignificantBits() ^ uuid.getMostSignificantBits();

冲突源于散列码是int(32位,四分之一),与其他属性组合不一定会使数字更随机;甚至更少。UUID是唯一的,但使用128位(两个长)

最好使用数据库键,例如字符串表示法。对于不太安全的

return uuid.getLeastSignificantBits() ^ uuid.getMostSignificantBits();

您的冲突源于哈希代码是整数(32位,四分之一),与其他属性相结合不一定会使数字更随机;甚至更不随机。

您当前的方法几乎没有问题:

  • java.util.Random
    不是线程安全的。如果在不同线程上对同一
    Random
    对象有3000个并发请求,则可能会出现奇怪的行为。请改为尝试
    ThreadLocalRandom

  • 如果3000个并发请求同时发生,则生成的
    System.currentTimeMillis()
    值将非常接近

  • 如果需要唯一标识符,请不要使问题过于复杂:

  • 使用UUID v4(
    UUID.randomUUID()
    ),或者如果您需要更强大的保证UUID v1。UUID中有足够的随机位来保证这一点
  • 如果您需要更简洁的东西,请维护一个共享计数器,例如数据库序列。每次需要新的唯一编号时,只需增加一个即可

  • 您当前的方法几乎没有问题:

  • java.util.Random
    不是线程安全的。如果在不同线程上对同一
    Random
    对象有3000个并发请求,则可能会出现奇怪的行为。请改为尝试
    ThreadLocalRandom

  • 如果3000个并发请求同时发生,则生成的
    System.currentTimeMillis()
    值将非常接近

  • 如果需要唯一标识符,请不要使问题过于复杂:

  • 使用UUID v4(
    UUID.randomUUID()
    ),或者如果您需要更强大的保证UUID v1。UUID中有足够的随机位来保证这一点
  • 如果您需要更简洁的东西,请维护一个共享计数器,例如数据库序列。每次需要新的唯一编号时,只需增加一个即可

  • 问:为什么您的方案具有显著的非唯一性

    有一只臭虫在吼叫

        private static Long abs(Long number){
            if(null == number || 0 < number){
                return 0L;
            }
            return Math.abs(number);
        }
    
    但是由于您的错误,第一个术语为零的概率为50%,最后一个术语为零的概率为50%

    因此有25%的概率,您的“随机”数将是以毫秒为单位的当前时间!现在假设您在毫秒内调用
    generateUniqueNum()
    两次

    哎呀


    请注意,如果没有此错误,您的代码生成的任何一对数字仍然有1/264的机会相等。如果您将此与“生日悖论”分析相结合,则任何冲突的概率都会变得显著。

    Q:为什么您的方案会给出显著的非唯一性

    有一只臭虫在吼叫

        private static Long abs(Long number){
            if(null == number || 0 < number){
                return 0L;
            }
            return Math.abs(number);
        }
    
    但是由于您的错误,第一个术语为零的概率为50%,最后一个术语为零的概率为50%

    因此有25%的概率,您的“随机”数将是以毫秒为单位的当前时间!现在假设您在毫秒内调用
    generateUniqueNum()
    两次

    哎呀


    请注意,如果没有这个bug,您的代码仍有可能在264分之一的情况下,生成的任何一对数字都是相等的通过分析,任何冲突的概率都变得非常重要。

    不要向UUID添加任何内容:它本身保证是唯一的。删除
    System.currentTimeMillis()
    。不要在UUID上使用
    hashCode
    :使用
    toString
    UUID.randomUUID().toString()
    足够了。另外,您的
    abs
    方法有缺陷:如果数字>0,则返回0,否则返回Math.abs???您确定此算法吗?您仍在UUID上使用hashcode:这不起作用…使用:
    UUID.randomUUID().toString()
    通过执行
    +abs(random.nextLong())
    在此之前,您基本上可以扔掉所有东西;您的“唯一”数字将只是一个(或多或少)随机长数。不要向UUID添加任何东西:它本身保证是唯一的。删除
    System.currentTimeMillis()
    。不要在UUID上使用
    hashCode
    :使用
    toString
    UUID.randomUUID().toString()
    就足够了。另外,您的
    abs
    方法有缺陷:如果数字>0,则返回0,否则返回Math.abs???您确定此算法吗?您仍然在UUID上使用hashcode:这不起作用…使用:
    UUID.randomUUID().toString()
    通过执行
    +abs(random.nextLong())
    在此之前,您基本上可以扔掉所有东西;您的“唯一”数将只是一个(或多或少)随机长数……最重要的是,不能保证hashCode均匀分布……
    public int hashCode(){return 42;}
    是一个完全有效的实现……最重要的是,不能保证hashCode是均匀分布的……
    public int hashCode(){return 42;}
    是一个完全有效的实现。