UUID不失精度,長度改進

在使用到uuid的時候,往往頭疼於它的長度(如1bfe50d8-544e-4e8a-95b8-199ceff15268),於是乎就有了改寫uuid的各種方法

1.去除“-”的uuid
不覺得uuid很長,但是就是看着中間的“-”很難受,又佔長度,簡單直接點就是

UUID uuid = UUID.randomUUID();
uuid.toString.replace("-", "");

額,這種方法,簡單粗暴不優雅,其實呢,還可以看看這個“-”是哪裏來的:

    public String toString() {
        return (digits(mostSigBits >> 32, 8) + "-" +
                digits(mostSigBits >> 16, 4) + "-" +
                digits(mostSigBits, 4) + "-" +
                digits(leastSigBits >> 48, 4) + "-" +
                digits(leastSigBits, 12));
    }
    /** Returns val represented by the specified number of hex digits. */
    private static String digits(long val, int digits) {
        long hi = 1L << (digits * 4);
        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
    }

源碼裏寫的很清楚 是它自己乾的,所以完全可以自己實現把“-”去掉(最終代碼在後面)

2.21-22位的uuid
去掉“-”之後變成了9b8a013583ba42cba75a9f3d6471eb7a,是一個16進制的字符串,但還是太長

    /*
     * The most significant 64 bits of this UUID.
     *
     * @serial
     */
    private final long mostSigBits;

    /*
     * The least significant 64 bits of this UUID.
     *
     * @serial
     */
    private final long leastSigBits;

源碼中的UUID類中的這兩個long型屬性(mostSigBits是前半部分,leastSigBits是後半部分),其實就代表了uuid,具體的字符串編碼都是通過這兩個long拼接起來的(不得不說,想法很雞賊,正常看到的就是這兩個的16進制字符串)。
有人說,那直接把“-”去掉使用base64轉化成64進制的字符串不就短了很多了?是這樣的,不過我們可以仿寫base64的實現寫個簡單的(主要base64最後是拿“+”和“/”湊的64個,“/”在http傳輸中容易被誤解析)

最終的UUIDUtils代碼:

import java.util.Date;
import java.util.UUID;

/**
 * Created by Kowalski on 2017/5/11
 * Updated by Kowalski on 2017/5/11
 */
public final class UUIDUtils {


    /**
     * 採用URL Base64字符,即把“+/”換成“-_”
     */
    private static final char[] digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_=".toCharArray();

    /**21-22位UUID*/
    public static String generateMost22UUID() {

        UUID uid = UUID.randomUUID();
        long most = uid.getMostSignificantBits();

        char[] buf = new char[22];
        int charPos = 22;
        int radix = 1 << 6;
        long mask = radix - 1;
        do {
            charPos--;
            buf[charPos] = digits[(int)(most & mask)];
            most >>>= 6;
        } while (most != 0);

        long least = uid.getLeastSignificantBits();
        do {
            charPos--;
            buf[charPos] = digits[(int)(least & mask)];
            least >>>= 6;
        } while (least != 0);
        return new String(buf, charPos, 22-charPos);
    }

    /**無 - UUID*/
    public static String generateUUID() {
        UUID uuid = UUID.randomUUID();
        long most = uuid.getMostSignificantBits();

        long least = uuid.getLeastSignificantBits();

        return (digits(most >> 32, 8) +
                digits(most >> 16, 4) +
                digits(most, 4) +
                digits(least >> 48, 4) +
                digits(least, 12));
    }

    private static String digits(long val, int digits) {
        long hi = 1L << (digits << 2);
        return Long.toHexString(hi | (val & (hi - 1)));
    }

    /**22位UUID*/
    public static String generateUUID22() {
        UUID uuid = UUID.randomUUID();
        long msb = uuid.getMostSignificantBits();
        long lsb = uuid.getLeastSignificantBits();
        char[] out = new char[24];
        int tmp = 0, idx = 0;
        // 循環寫法
        int bit = 0, bt1 = 8, bt2 = 8;
        int mask = 0x00, offsetm = 0, offsetl = 0;

        for(; bit < 16; bit += 3, idx += 4) {
            offsetm = 64 - ((bit + 3) << 3);
            offsetl = 0;
            tmp = 0;

            if(bt1 > 3) {
                mask = (1 << 8 * 3) - 1;
            } else if(bt1 >= 0) {
                mask = (1 << 8 * bt1) - 1;
                bt2 -= 3 - bt1;
            } else {
                mask = (1 << 8 * ((bt2 > 3) ? 3 : bt2)) - 1;
                bt2 -= 3;
            }
            if(bt1 > 0) {
                bt1 -= 3;
                tmp = (int) ((offsetm < 0) ? msb : (msb >>> offsetm) & mask);
                if(bt1 < 0) {
                    tmp <<= Math.abs(offsetm);
                    mask = (1 << 8 * Math.abs(bt1)) - 1;
                }
            }
            if(offsetm < 0) {
                offsetl = 64 + offsetm;
                tmp |= ((offsetl < 0) ? lsb : (lsb >>> offsetl)) & mask;
            }

            if(bit == 15) {
                out[idx + 3] = digits[64];
                out[idx + 2] = digits[64];
                tmp <<= 4;
            } else {
                out[idx + 3] = digits[tmp & 0x3f];
                tmp >>= 6;
                out[idx + 2] = digits[tmp & 0x3f];
                tmp >>= 6;
            }
            out[idx + 1] = digits[tmp & 0x3f];
            tmp >>= 6;
            out[idx] = digits[tmp & 0x3f];
        }

        return new String(out, 0, 22);
    }

    public static void main(String... args) {

        Date d5 = new Date();
        for(int i = 0; i < 10000000; i++) {
            generateUUID22();
        }
        Date d6 = new Date();
        System.out.print(d6.getTime() - d5.getTime());
        System.out.println("\n");

        Date d1 = new Date();
        for(int i = 0; i < 10000000; i++) {
            generateMost22UUID();
        }
        Date d2 = new Date();
        System.out.print(d2.getTime() - d1.getTime());
        System.out.println("\n");
    }

}

這種實現方式比用replace後再用base64轉換速度要更快(接近一倍),這裏都是爲了保證uuid的精度實現的,對uuid精度要求較低的也可以使用其他位數更少的uuid變體,有更好方案的小夥伴來交流~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章