1. 程式人生 > >生成唯一標識碼的兩種方法

生成唯一標識碼的兩種方法

方法一:
答:UUID 的目的是讓分散式系統中的所有元素,都能有唯一的辨識資訊,而不需要透過中央控制端來做辨識資訊的指定。如此一來,每個人都可以建立不與其它人衝突的 UUID(通用唯一標識碼)。
UUID的組成:
1.當前日期和時間。
2.時鐘序列。 (數值 按其發生的先後順序而排列的數列)
3.全域性唯一的IEEE機器識別號。

缺點:UUID雖然可以保證全域性唯一,但是它佔用32位(十進位制),而且是無序的,入庫時效能比較差。
為什麼入庫效能比較差?
答:關係型資料庫的索引都是B+樹結構, 如果我們安照ID遞增的順序,新的結點會插入到最後一個
結點後面去,當最後一個結點滿了,會裂變出新的結點。
但如果是插入無序的,不僅會導致中間結點的裂變,還會產生很多不飽和節點,導致效能降低。

外部介面:StringUtils.randomUUID()

public class StringUtils {
    private static String[] binaryArray =   
        {"0000","0001","0010","0011",  
        "0100","0101","0110","0111",  
        "1000","1001","1010","1011",  
        "1100","1101","1110","1111"};  
//    private static String[] chineseDigits = new String[] { "零", "壹", "貳", "叄", "肆", "伍", "陸", "柒", "捌", "玖"};
public static String[] chineseDigits = new String[] { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}; private static final char[] charBytes = { '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'}; private static final char[] numberBytes = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; /** * 生成指定位數的隨機數子. * @param number * @return */ public static String randomNumbers(int number) { int count = 0; //生成的密碼的長度 int i; //生成的隨機數 final int maxNum = numberBytes.length; StringBuffer randomStr = new StringBuffer(""); Random r = new Random(); while(count < number){ //生成隨機數,取絕對值,防止生成負數, i = Math.abs(r.nextInt(maxNum)); //生成的數最大為36-1 if (i >= 0 && i < numberBytes.length) { randomStr.append(numberBytes[i]); count ++; } } return randomStr.toString(); } public static String randomStrByNumber(int number) { int count = 0; //生成的密碼的長度 int i; //生成的隨機數 final int maxNum = charBytes.length; StringBuffer randomStr = new StringBuffer(""); Random r = new Random(); while(count < number){ //生成隨機數,取絕對值,防止生成負數, i = Math.abs(r.nextInt(maxNum)); //生成的數最大為36-1 if (i >= 0 && i < charBytes.length) { randomStr.append(charBytes[i]); count ++; } } return randomStr.toString(); } public static String randomUUID() { UUID uuid = UUID.randomUUID(); return uuid.toString().replace("-", "").toUpperCase(); } public static String digitsTochinese(int i){ //大於10的需要重寫 return chineseDigits[i]; } public static String toAllUpperCase(String uuid) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < uuid.length(); i++) { char c = uuid.charAt(i); if (Character.isLowerCase(c)) { buffer.append(Character.toUpperCase(c)); } else { buffer.append(c); } } return buffer.toString(); } // 十六進位制字串轉byte陣列 public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } private static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } // 陣列轉字串、以空格間隔 public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } if (i == src.length - 1) { stringBuilder.append(hv); } else { stringBuilder.append(hv); } } return stringBuilder.toString(); } public static String bytesToHexStringNoAppendBit(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); /*if (hv.length() < 2) { stringBuilder.append(0); }*/ if (i == src.length - 1) { stringBuilder.append(hv); } else { stringBuilder.append(hv); } } return stringBuilder.toString(); } public static String bytesToString(byte[] src, String split) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = String.valueOf(v); if (i == src.length - 1) { stringBuilder.append(hv); } else { stringBuilder.append(hv + split); } } return stringBuilder.toString(); } public static String generateHexString(int paramInt) { StringBuffer localStringBuffer = new StringBuffer(); Random localRandom = new Random(); int i = 16; for (int j = 0; j < paramInt; j++) { if (j % 2 == 0) { localStringBuffer.append("0123456789ABCDEF".charAt(localRandom .nextInt(i))); } else { localStringBuffer.append("0123456789ABCDEF".charAt(localRandom .nextInt(i)) + " "); } } return localStringBuffer.toString(); } public static byte[] decodeTripleDES(byte[] data, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { byte[] keys; if (key.length == 16) { keys = new byte[24]; System.arraycopy(key, 0, keys, 0, 16); System.arraycopy(key, 0, keys, 16, 8); } else { keys = key; } Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); SecretKey secretKey = new SecretKeySpec(keys, "DESede"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(data); } public static boolean equals(byte[] b1, byte[] b2) { if (b1.length != b2.length) { return false; } for (int i = 0; i < b1.length; i++) { if (b1[i] != b2[i]) { return false; } } return true; } /** * * @return 轉換為二進位制字串 */ public static String bytes2BinaryStr(byte[] bArray){ String outStr = ""; int pos = 0; for(byte b:bArray){ //高四位 pos = (b&0xF0)>>4; outStr+=binaryArray[pos]; //低四位 pos=b&0x0F; outStr+=binaryArray[pos]; } return outStr; } /**將二進位制轉換成16進位制   * @param buf   * @return   */ public static String binaryToHexString(byte[] bytes) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(bytes[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /**將16進位制轉換為二進位制   * @param hexStr   * @return   */ public static byte[] hexStringToBinary(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length()/2]; for (int i = 0;i< hexStr.length()/2; i++) { int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); result[i] = (byte) (high * 16 + low); } return result; } //十六進位制轉為字元 public static String hexStringToString(String s) { byte[] baKeyword = new byte[s.length() / 2]; for (int i = 0; i < baKeyword.length; i++) { try { baKeyword[i] = (byte) (0xff & Integer.parseInt( s.substring(i * 2, i * 2 + 2), 16)); } catch (Exception e) { e.printStackTrace(); } } try { s = new String(baKeyword, "utf-8");// UTF-16le:Not } catch (Exception e1) { e1.printStackTrace(); } return s; } /** * 給某個日期加幾天後的日期 eg:2013-06-28號+1天是 2013-06-29 ,+3天是2013-07-01 * @param date 日期 * @param dump 數字 * @return */ public static String getDateByAddSomeDay(Date date,int dump){ Calendar ca=Calendar.getInstance(); SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd"); //構造任意格式 String today = sm.format(date); String[] timeArray= today.split("-"); ca.set(Calendar.YEAR,Integer.parseInt(timeArray[0])); ca.set(Calendar.MONTH, Integer.parseInt(timeArray[1])-1); ca.set(Calendar.DAY_OF_MONTH,Integer.parseInt(timeArray[2])); ca.add(Calendar.DAY_OF_MONTH, dump); String someDay = sm.format(ca.getTime()); return someDay; } /** * 生成公鑰 * @param pubkey * @return * add by yaoyuan */ public static String generatePublicKey(String pubkey) { // BASE64Encoder base64en = new BASE64Encoder(); // String encode = base64en.encode(hexStringToBinary(pubkey)); // encode = "-----BEGIN RSA PUBLIC KEY-----" + encode + "-----END RSA PUBLIC KEY-----"; // if (encode.getBytes().length < 256) { // encode = org.apache.commons.lang.StringUtils.rightPad(encode, 256, "0"); // } // return encode; return null; } /** * 獲取當前時間 精確到毫秒 */ public static String getCurrentTime(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String currentTime = sdf.format(new Date()); return currentTime; } /** * @功能: BCD碼轉為10進位制串(阿拉伯資料) * @引數: BCD碼 * @結果: 10進位制串 */ public static String bcd2Str(byte[] bytes) { StringBuffer temp = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { temp.append((byte) ((bytes[i] & 0xf0) >>> 4)); temp.append((byte) (bytes[i] & 0x0f)); } return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp .toString().substring(1) : temp.toString(); } /** * @功能: 10進位制串轉為BCD碼 * @引數: 10進位制串 * @結果: BCD碼 */ public static byte[] str2Bcd(String asc) { int len = asc.length(); int mod = len % 2; if (mod != 0) { asc = "0" + asc; len = asc.length(); } byte abt[] = new byte[len]; if (len >= 2) { len = len / 2; } byte bbt[] = new byte[len]; abt = asc.getBytes(); int j, k; for (int p = 0; p < asc.length() / 2; p++) { if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) { j = abt[2 * p] - '0'; } else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) { j = abt[2 * p] - 'a' + 0x0a; } else { j = abt[2 * p] - 'A' + 0x0a; } if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) { k = abt[2 * p + 1] - '0'; } else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) { k = abt[2 * p + 1] - 'a' + 0x0a; } else { k = abt[2 * p + 1] - 'A' + 0x0a; } int a = (j << 4) + k; byte b = (byte) a; bbt[p] = b; } return bbt; } /** * 去除字串中的符合${}形式的子串. * @param * @return 去除${}的字串 */ public static String escape$String(String input) { return input.replaceAll("\\$\\{[^}]*\\}", ""); } public static String replace$String(String input, String newStr) { return input.replaceAll("\\$\\{[^}]*\\}", newStr); } public static String replace$SpecifyString(String input, String str, String newStr) { if(input != null){ input = input.replaceAll("\\$\\{" + str + "\\}", newStr); } return input; } public static String replace$String(Map<String, Object> map, String src) { if (src != null && map != null) { for (String key : map.keySet()) { String value = String.valueOf(map.get(key)); src = replace$SpecifyString(src, key, value); } } return src; } /** * 生成驗證碼 * @return */ public static String createValidateCode(){ String str = "0,1,2,3,4,5,6,7,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,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"; String str2[] = str.split(",");//將字串以,分割 String code=""; for(int i=0;i<4;i++){ int a=(int)(Math.random()*36); code=code+str2[a]; } return code; } /** * 根據身份證計算年齡 * @param idNumber * @return */ public static int calculateAgeByIdNumber(String idNumber){ int leh = idNumber.length(); int years =0; if (leh == 18) { years=Integer.parseInt(idNumber.substring(6, 10)); }else { years = Integer.parseInt("19" + idNumber.substring(6, 8)); } Calendar a=Calendar.getInstance(); return a.get(Calendar.YEAR)-years; } /** * 根據身份證計算性別 * @param idNumber * @return */ public static int calculateSexByIdNumber(String idNumber){ int leh = idNumber.length(); int se =0; if (leh == 18) { se=Integer.valueOf(idNumber.substring(16,17)) % 2; } return (se==1?1:2); } /** * 查詢數字在陣列中得區間. * @param sortedData 排序陣列 * @param findValue * @return */ public static int searchValueSectionInArr(Integer[] sortedData,int findValue) { int start = 0; int end = 0; for (int i = 0; i < sortedData.length; i++) { if (findValue >= sortedData[i]) { start = i; } else { end = i; break; } } return start; } /** * 迴圈二分查詢陣列區間,返回第一次出現該值的位置 * @param sortedData 已排序的陣列 * @param findValue 需要找的值 * @return 值在陣列中的位置,從0開始。找不到返回-1 */ public static int binarySearch(Integer[] sortedData,int findValue) { int start=0; int end=sortedData.length-1; while(start<=end) { //中間位置 int middle=(start+end)>>1; //相當於(start+end)/2 // System.out.println("middle==>>" + middle); //中值 int middleValue=sortedData[middle]; if(findValue < middleValue) { //小於中值時在中值前面找 end = middle-1; //如果小於前邊的值 和前 前邊的值 則繼續下一次查詢 if (end >= 0) { int end_v =sortedData[end]; if (findValue >= end_v) { return end; } } else { return middle; } } else { //大於中值在中值後面找 start=middle+1; if (start <= sortedData.length -1) { int end_v = sortedData[start]; if (findValue < end_v) { return middle; } } else { return middle; } } } //找不到 return -1; } }

方法二:
SnowFlake演算法:分散式系統中生成全域性唯一且趨勢遞增的Id.
這裡寫圖片描述
總共64位2進位制,換成十進位制為18位
第一部分: 1位,始終為0.
第二部分:41位,精確為毫秒的時間戳。
第三部分:10位,機器碼
第四部分:12位,序列號

snowflake的優點:
1.是按ID遞增,易於插入到資料庫
2.不依賴資料庫,在記憶體中生成,效能好

注:建構函式(工作機器ID,資料中心ID)

public class SnowflakeIdWorker {
    // ==============================Fields===========================================
    /** 開始時間截 (2015-01-01) */
    private final long twepoch = 1420041600000L;
    /** 機器id所佔的位數 */
    private final long workerIdBits = 5L;
    /** 資料標識id所佔的位數 */
    private final long datacenterIdBits = 5L;
    /** 支援的最大機器id,結果是31 (這個移位演算法可以很快的計算出幾位二進位制數所能表示的最大十進位制數) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /** 支援的最大資料標識id,結果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    /** 序列在id中佔的位數 */
    private final long sequenceBits = 12L;
    /** 機器ID向左移12位 */
    private final long workerIdShift = sequenceBits;
    /** 資料標識id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    /** 時間截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    /** 生成序列的掩碼,這裡為4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    /** 工作機器ID(0~31) */
    private long workerId;
    /** 資料中心ID(0~31) */
    private long datacenterId;
    /** 毫秒內序列(0~4095) */
    private long sequence = 0L;
    /** 上次生成ID的時間截 */
    private long lastTimestamp = -1L;
    //==============================Constructors=====================================
    /**
     * 建構函式
     * @param workerId 工作ID (0~31)
     * @param datacenterId 資料中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    // ==============================Methods==========================================
    /**
     * 獲得下一個ID (該方法是執行緒安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        //如果當前時間小於上一次ID生成的時間戳,說明系統時鐘回退過這個時候應當丟擲異常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        //如果是同一時間生成的,則進行毫秒內序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒內序列溢位
            if (sequence == 0) {
                //阻塞到下一個毫秒,獲得新的時間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //時間戳改變,毫秒內序列重置
        else {
            sequence = 0L;
        }
        //上次生成ID的時間截
        lastTimestamp = timestamp;
        //移位並通過或運算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }
    /**
     * 阻塞到下一個毫秒,直到獲得新的時間戳
     * @param lastTimestamp 上次生成ID的時間截
     * @return 當前時間戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    /**
     * 返回以毫秒為單位的當前時間
     * @return 當前時間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
    //==============================Test=============================================
    /** 測試 */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            //轉換成二進位制
            System.out.println(Long.toBinaryString(id));
            System.out.println(id);
        }
    }
}