上面的方法toCanonicalString()
看起来很"臃肿" , 但是能保证性能比较高 , 实现思路来自于Long#fastUUID()
, 也就是UUID
的五段格式化方法 。借鉴并且简化一下可以抽取一个toCanonicalString0()
方法:
public String toCanonicalString0() {byte[] bytes = new byte[ULID_CHAR_LEN];formatUnsignedLong0(this.lsb & 0xffffffffffL, 5, bytes, 18, 8);formatUnsignedLong0(((this.msb & 0xffffL) << 24) | (this.lsb >>> 40), 5, bytes, 10, 8);formatUnsignedLong0(this.msb >> 16, 5, bytes, 0, 10);return new String(bytes, StandardCharsets.US_ASCII);}private static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) {int charPos = offset + len;long radix = 1L << shift;long mask = radix - 1;do {buf[--charPos] = (byte) DEFAULT_ALPHABET[(int) (val & mask)];val >>>= shift;} while (charPos > offset);}
toCanonicalString0()
方法和toString()
方法会得到相同的ULID
格式化字符串 。接着添加常用的工厂方法:
public static ULID ulid() {return ulid(System::currentTimeMillis, len -> {byte[] bytes = new byte[len];ThreadLocalRandom.current().nextBytes(bytes);return bytes;});}public static ULID ulid(Supplier<Long> timestampProvider,IntFunction<byte[]> randomnessProvider) {return new ULID(timestampProvider.get(), randomnessProvider.apply(RANDOMNESS_BYTE_LEN));}
默认使用ThreadLocalRandom
生成随机数 , 如果是JDK17
以上 , 还可以选用更高性能的新型PRNG
实现 , 对应接口是RandomGenerator
, 默认实现是L32X64MixRandom
。编写一个main
方法运行一下:
public static void main(String[] args) {System.out.println(ULID.ulid());}// 某次执行结果01GFGGMBFGB5WKXBN7S84ATRDG
最后实现"单调递增"的ULID
构造 , 先提供一个"增长"方法:
/*** The least significant 64 bits increase overflow, 0xffffffffffffffffL + 1*/private static final long OVERFLOW = 0x0000000000000000L;public ULID increment() {long msb = this.msb;long lsb = this.lsb + 1;if (lsb == OVERFLOW) {msb += 1;}return new ULID(msb, lsb);}
其实就是低位加1
, 溢出后高位加1
。接着添加一个静态内部子类和响应方法如下:
// 构造函数public ULID(ULID other) {this.msb = other.msb;this.lsb = other.lsb;}public static byte[] defaultRandomBytes(int len) {byte[] bytes = new byte[len];ThreadLocalRandom.current().nextBytes(bytes);return bytes;}public static MonotonicULIDSpi monotonicUlid() {return monotonicUlid(System::currentTimeMillis, ULID::defaultRandomBytes);}public static MonotonicULIDSpi monotonicUlid(Supplier<Long> timestampProvider,IntFunction<byte[]> randomnessProvider) {return new MonotonicULID(timestampProvider, randomnessProvider, timestampProvider.get(),randomnessProvider.apply(RANDOMNESS_BYTE_LEN));}// @SPI MonotonicULIDpublic interface MonotonicULIDSpi {ULID next();}private static class MonotonicULID extends ULID implements MonotonicULIDSpi {@Serialprivate static final long serialVersionUID = -9158161806889605101L;private volatile ULID lastULID;private final Supplier<Long> timestampProvider;private final IntFunction<byte[]> randomnessProvider;public MonotonicULID(Supplier<Long> timestampProvider,IntFunction<byte[]> randomnessProvider,long timestamp,byte[] randomness) {super(timestamp, randomness);this.timestampProvider = timestampProvider;this.randomnessProvider = randomnessProvider;this.lastULID = new ULID(timestamp, randomness);}// 这里没设计好 , 子类缓存了上一个节点 , 需要重写一下increment方法 , 父类可以移除此方法@Overridepublic ULID increment() {long newMsb = lastULID.msb;long newLsb = lastULID.lsb + 1;if (newLsb == OVERFLOW) {newMsb += 1;}return new ULID(newMsb, newLsb);}@Overridepublic synchronized ULID next() {long lastTimestamp = lastULID.getTimestamp();long timestamp = getTimestamp();// 这里做了一个恒为true的判断 , 后面再研读其他代码进行修改if (lastTimestamp >= timestamp || timestamp - lastTimestamp <= 1000) {this.lastULID = this.increment();} else {this.lastULID = new ULID(timestampProvider.get(), randomnessProvider.apply(RANDOMNESS_BYTE_LEN));}return new ULID(this.lastULID);}}
经验总结扩展阅读
- vite vue3 规范化与Git Hooks
- 钩子 【pytest官方文档】解读-插件开发之hooks 函数
- 深渊游戏结局解读
- 如何解读芥川龙之介的秋山图
- 襄怎么读
- 中 ?打造企业自己代码规范IDEA插件
- 脚手架规范有哪些
- HashMap底层原理及jdk1.8源码解读
- 规范正交基和标准正交基一样吗
- JS 模块化-05 ES Module & 4 大规范总结