写点什么

UUID 不失精度,长度改进

发布于: 2021 年 03 月 31 日

在使用到 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 变体,有更好方案的小伙伴来交流~

发布于: 2021 年 03 月 31 日阅读数: 11
用户头像

还未添加个人签名 2020.03.30 加入

还未添加个人简介

评论

发布
暂无评论
UUID不失精度,长度改进