巧借Long类实现自己的UUID生成

本文介绍了一种使用时间戳加随机数生成唯一主键的方法,并提供了一个Java工具类实现。该方法能够有效避免ID泄露问题,适用于并发量适中的场景。

        在使用数据库的时候,经常需要生成主键,用过数据库的自增长,但ID太有序了,很容易就能让人知道下一条记录的ID。也用过Hibernate的UUID生成,但生成的主键似乎又太长了点,于是想着自己写一个工具来实现。

       首先想到的便是用时间戳+随机码的方式,对于并发量不是特别特别特别高的情况来说,基本是够用的,于是我一般都用Long.parseLong(""+(int)(Math.random()*100)+(new Date()).getTime()+(int)(Math.random()*100))的方式来生成一个Long型的值,但这个值一来太长了,二来很容易给人找出规律,毕竟时间戳部分的前几位是一致的,于是考虑到将这个Long值转成类似于16进制之类的字符串。不过数字+字母(分大小写)一共有62位,16进制只用到了其中的一部分,还有大把没用的,有点浪费。在参考了Long类的toHexString()方法源码后,自己改写了一个工具类。

 

package com.hing.tools;

import java.util.Date;

/**
 * author:hingwu email:hing3@163.com QQ:550598 MSN:hing3wu@hotmail.com(很少开)
 * 
 * 2007-3-28 上午11:40:18
 
*/

public class MyLong {
    
public static final long MIN_VALUE = 0x8000000000000000L;

    
public static final long MAX_VALUE = 0x7fffffffffffffffL;

    
final static char[] digits = '0''1''2''3''4',
            
'5''6''7''8''9''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''-''_' }
;

    
private static String toUnsignedString(long i, int shift) {
        
char[] buf = new char[64];
        
int charPos = 64;
        
int radix = 1 << shift;
        
long mask = radix - 1;
        
do {
            buf[
--charPos] = digits[(int) (i & mask)];
            i 
>>>= shift;
        }
 while (i != 0);
        
return new String(buf, charPos, (64 - charPos));
    }

    
     
     
// j为2的次方,如转成16进制就是4,32进制就是5...
     public static String getRand(long i,int j){
         
return toUnsignedString(i, j); 
     }

     
     
// 随机码+时间戳+随机码的生成
     public static Long getRand(){
         String str1,str2,str3;
         str1
=getRandStr(2);
         str3
=getRandStr(3);
         str2
=(new Date()).getTime()+"";
         
//System.out.println(str1+str2+str3);
         return Long.parseLong(str1+str2+str3);
     }

     
     
// 主键生成
     public static String getKey(){
         
return getRand(getRand(),6);
     }

     
     
//    生成指定长度的随机串
     public static String getRandStr(Integer length){
         String str
="";
         
while(str.length()!=length){
             str
=(Math.random()+"").substring(2,2+length);
         }

         
return str;
     }

}

 

测试类:

 

package com.test;

import java.util.Date;

import com.hing.tools.MyLong;



/**
 *    author:hingwu
 *  email:hing3@163.com
 *  QQ:550598
 *  MSN:hing3wu@hotmail.com(很少开) 
 *
 * 2007-3-15 下午04:37:26
 
*/

public class Test {

    
/**
     * 
@param args
     * 
@throws IOException 
     * 
@throws InterruptedException 
     
*/

    
public static void main(String[] args){
        
// TODO Auto-generated method stub
        for(int i=0;i<10;i++){
             System.out.println("第"+i+"个:"+MyLong.getKey());

        }


    }

}


测试结果:

第0个:qK43fCDV
第1个:KCAZxkKE4
第2个:47knOADSDV
第3个:lKZzvVmDQ
第4个:4RweI2I-Ek
第5个:3eup9odSEr
第6个:5p1ZROteDU
第7个:bN0kVHKgP
第8个:2lEqwbPSDE
第9个:7xtVcOSEh

       如果把digits中的字符顺序打乱的话,就更难给人找到规律了。 

<think> 在Java中生成Long类型UUID,通常意味着我们需要从一个标准的UUID(128位)中提取部分位来组成一个Long值(64位)。但请注意,UUID是128位的,而Long是64位的,因此会有信息丢失。常见的做法是取UUID的高64位或低64位,或者通过哈希函数将其压缩到64位。 然而,UUID本身由两个long值组成:most significant bits (MSB) 和 least significant bits (LSB)。我们可以使用其中一个(比如MSB)作为Long类型UUID,但这并不能保证唯一性(因为UUID生成依赖于整个128位)。另一种方式是使用UUID的hashCode(),但hashCode是32位整数,不适合。或者使用UUID的getMostSignificantBits()或getLeastSignificantBits(),但这只能得到一半的信息。 另一种思路是生成一个随机数作为Long,但这样就不是UUID了。所以需要明确:我们是要一个Long类型的唯一标识符,还是要把UUID转换成Long类型? 通常,我们可能希望生成一个唯一的Long值。如果我们不需要严格的UUID格式,可以使用随机数生成生成64位的随机数(但随机数可能重复,需要根据场景考虑)。如果要求较高,可以使用时间戳+随机数等方式。 但是,如果要求基于UUID生成,那么我们可以取UUID的高64位(most significant bits)或低64位(least significant bits),或者将两个long进行异或等操作合并为一个long(但这样仍然可能损失唯一性)。 考虑到UUID的版本(如版本4是随机生成的),取其中64位仍然有较高的唯一性,但不如128位。因此,我们可以这样生成: 方法1:使用UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE 来确保正数(因为Long在Java中是有符号的,而有时我们希望得到一个正数,但这不是必须的)。 方法2:使用UUID.randomUUID().getLeastSignificantBits() 但是注意,getMostSignificantBits()或getLeastSignificantBits()单独使用都不能保证全局唯一,因为它们只是UUID的一部分。 方法3:将两个64位合并成一个64位?这显然不可能,因为两个64位无法无损压缩为一个64位。所以只能取一部分。 因此,我们通常的做法是取其中的一部分。例如: ```java long uuidLong = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; ``` 这里使用`& Long.MAX_VALUE`是为了避免得到负数,因为Long的最高位是符号位,这样操作可以确保得到一个非负数(但仍然是63位有效位,最高位为0)。如果允许负数,可以不加。 但是,请注意,这样生成Long值并不是一个标准的UUID,而是UUID的一部分。 另外,如果我们需要一个全局唯一的Long,可以考虑使用其他方法,比如分布式ID生成算法(如Snowflake算法)来生成64位的唯一ID。 所以,根据需求,如果只是需要一个Long类型的随机唯一ID,并且可以接受较低的全局唯一性(因为64位随机数在生成数量不多的情况下重复概率较低),我们可以使用上述方法。如果对唯一性要求很高,则建议使用专门的分布式ID生成算法。 下面是一个生成Long类型随机ID的示例代码: ```java import java.util.UUID; public class LongUUIDGenerator { public static long generateLongUUID() { // 取UUID的高64位,并确保非负 return UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; } } ``` 但是请注意,这种方法生成Long值有可能重复(尽管概率很低),因此如果用于高并发的唯一标识,请评估其重复概率是否在可接受范围内。 另外,我们也可以取低64位: ```java public static long generateLongUUID() { return UUID.randomUUID().getLeastSignificantBits(); } ``` 或者结合两者(例如异或): ```java public static long generateLongUUID() { UUID uuid = UUID.randomUUID(); return uuid.getMostSignificantBits() ^ uuid.getLeastSignificantBits(); } ``` 但无论哪种方法,都无法保证绝对唯一。 因此,根据问题的要求,我们给出一种常见的实现:取UUID的高64位,并转换为非负数。 现在,根据规则,在回答的最后提出3-5个相关问题。 </think>在 Java 中生成 `Long` 类型UUID,可以通过截取标准 UUID 的部分位来实现。以下是两种常用方法: ### 方法 1:取 UUID 的高 64 位 ```java import java.util.UUID; public class LongUuidGenerator { public static long generateLongUuid() { UUID uuid = UUID.randomUUID(); return uuid.getMostSignificantBits(); } } ``` ### 方法 2:取 UUID 的低 64 位 ```java import java.util.UUID; public class LongUuidGenerator { public static long generateLongUuid() { UUID uuid = UUID.randomUUID(); return uuid.getLeastSignificantBits(); } } ``` ### 注意事项: 1. **非全局唯一性**:Long (64位) 无法完全容纳 UUID (128位),截取部分位会损失唯一性保证(冲突概率极低但仍存在)。 2. **负数处理**:结果可能是负数(Long 在 Java 中是有符号类型),若需正整数可添加掩码: ```java return uuid.getMostSignificantBits() & Long.MAX_VALUE; // 确保非负 ``` 3. **应用场景**:适用于对唯一性要求不严格的场景(如临时标识),高并发关键系统建议使用完整 UUID
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值