mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
新增UUID v7生成器
编写对应的单元测试
This commit is contained in:
parent
21858539e5
commit
e90986ff19
@ -77,6 +77,10 @@ public class IdUtil {
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
|
||||
public static String randomUUID7() {
|
||||
return UUID.randomUUID7().toString();
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ----- ObjectId
|
||||
@ -259,7 +263,7 @@ public class IdUtil {
|
||||
}
|
||||
if (null != mac) {
|
||||
id = ((0x000000FF & (long) mac[mac.length - 2])
|
||||
| (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
|
||||
| (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
|
||||
id = id % (maxDatacenterId + 1);
|
||||
}
|
||||
|
||||
@ -305,7 +309,6 @@ public class IdUtil {
|
||||
* 则会使用此默认实例。
|
||||
*
|
||||
* @return SeataSnowflake 返回一个默认配置的SeataSnowflake实例。
|
||||
*
|
||||
* @see IdConstants#DEFAULT_SNOWFLAKE
|
||||
*/
|
||||
public static SeataSnowflake getSeataSnowflake() {
|
||||
@ -320,7 +323,6 @@ public class IdUtil {
|
||||
*
|
||||
* @param nodeId 节点ID
|
||||
* @return SeataSnowflake 返回一个自定义配置的SeataSnowflake实例。
|
||||
*
|
||||
* @see IdConstants#DEFAULT_SEATA_SNOWFLAKE
|
||||
*/
|
||||
public static SeataSnowflake getSeataSnowflake(final long nodeId) {
|
||||
|
@ -24,6 +24,7 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 提供通用唯一识别码(universally unique identifier)(UUID)实现,UUID表示一个128位的值。<br>
|
||||
@ -82,6 +83,9 @@ public class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
static final SecureRandom NUMBER_GENERATOR = RandomUtil.getSecureRandom();
|
||||
}
|
||||
|
||||
private static final AtomicLong lastV7time = new AtomicLong(0);
|
||||
private static final long NANOS_PER_MILLI = 1_000_000;
|
||||
|
||||
private final Number128 idValue;
|
||||
|
||||
/**
|
||||
@ -178,6 +182,62 @@ public class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
return new UUID(md5Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机生成的UUIDv7
|
||||
*
|
||||
* @return UUIDv7
|
||||
*/
|
||||
public static UUID randomUUID7() {
|
||||
byte[] randomBytes = new byte[16];
|
||||
final Random ng = Holder.NUMBER_GENERATOR;
|
||||
ng.nextBytes(randomBytes);
|
||||
|
||||
long[] v7Time = getV7Time();
|
||||
long milli = v7Time[0];
|
||||
long seq = v7Time[1];
|
||||
|
||||
randomBytes[0] = (byte) (milli >> 40);
|
||||
randomBytes[1] = (byte) (milli >> 32);
|
||||
randomBytes[2] = (byte) (milli >> 24);
|
||||
randomBytes[3] = (byte) (milli >> 16);
|
||||
randomBytes[4] = (byte) (milli >> 8);
|
||||
randomBytes[5] = (byte) milli;
|
||||
|
||||
randomBytes[6] = (byte) ((0x70) | (0x0F & (seq >> 8)));
|
||||
randomBytes[7] = (byte) seq;
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
|
||||
return new UUID(convertToLong(randomBytes, 0), convertToLong(randomBytes, 8));
|
||||
}
|
||||
|
||||
private static long[] getV7Time() {
|
||||
long nano = System.nanoTime();
|
||||
long milli = nano / NANOS_PER_MILLI;
|
||||
long seq = (nano - milli * NANOS_PER_MILLI) >> 8;
|
||||
long now = (milli << 12) + seq;
|
||||
|
||||
while (true) {
|
||||
long last = lastV7time.get();
|
||||
if (now <= last) {
|
||||
now = last + 1;
|
||||
milli = now >> 12;
|
||||
seq = now & 0xfff;
|
||||
}
|
||||
if (lastV7time.compareAndSet(last, now)) {
|
||||
return new long[]{milli, seq};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long convertToLong(byte[] bytes, int start) {
|
||||
long result = 0;
|
||||
for (int i = start; i < start + 8; i++) {
|
||||
result = (result << 8) | (bytes[i] & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
|
||||
*
|
||||
@ -453,7 +513,7 @@ public class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
// The ordering is intentionally set up so that the UUIDs
|
||||
// can simply be numerically compared as two numbers
|
||||
int compare = Long.compare(this.getMostSignificantBits(), val.getMostSignificantBits());
|
||||
if(0 == compare){
|
||||
if (0 == compare) {
|
||||
compare = Long.compare(this.getLeastSignificantBits(), val.getLeastSignificantBits());
|
||||
}
|
||||
return compare;
|
||||
|
@ -22,17 +22,21 @@ import org.dromara.hutool.core.data.id.Snowflake;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link IdUtil} 单元测试
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
public class IdUtilTest {
|
||||
|
||||
@ -101,9 +105,9 @@ public class IdUtilTest {
|
||||
//每个线程生成的ID数
|
||||
final int idCountPerThread = 10000;
|
||||
final CountDownLatch latch = new CountDownLatch(threadCount);
|
||||
for(int i =0; i < threadCount; i++) {
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
ThreadUtil.execute(() -> {
|
||||
for(int i1 = 0; i1 < idCountPerThread; i1++) {
|
||||
for (int i1 = 0; i1 < idCountPerThread; i1++) {
|
||||
final long id = snowflake.next();
|
||||
set.add(id);
|
||||
// Console.log("Add new id: {}", id);
|
||||
@ -131,9 +135,9 @@ public class IdUtilTest {
|
||||
//每个线程生成的ID数
|
||||
final int idCountPerThread = 10000;
|
||||
final CountDownLatch latch = new CountDownLatch(threadCount);
|
||||
for(int i =0; i < threadCount; i++) {
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
ThreadUtil.execute(() -> {
|
||||
for(int i1 = 0; i1 < idCountPerThread; i1++) {
|
||||
for (int i1 = 0; i1 < idCountPerThread; i1++) {
|
||||
final long id = IdUtil.getSnowflake(1, 1).next();
|
||||
set.add(id);
|
||||
// Console.log("Add new id: {}", id);
|
||||
@ -152,9 +156,80 @@ public class IdUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDataCenterIdTest(){
|
||||
public void getDataCenterIdTest() {
|
||||
//按照mac地址算法拼接的算法,maxDatacenterId应该是0xffffffffL>>6-1此处暂时按照0x7fffffffffffffffL-1,防止最后取模溢出
|
||||
final long dataCenterId = IdUtil.getDataCenterId(Long.MAX_VALUE);
|
||||
Assertions.assertTrue(dataCenterId >= 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUUIDv7Format() {
|
||||
org.dromara.hutool.core.data.id.UUID uuid = org.dromara.hutool.core.data.id.UUID.randomUUID7();
|
||||
String uuidStr = uuid.toString();
|
||||
|
||||
// 验证UUID字符串格式是否符合标准
|
||||
assertTrue(uuidStr.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUUIDv7Properties() {
|
||||
org.dromara.hutool.core.data.id.UUID uuid = org.dromara.hutool.core.data.id.UUID.randomUUID7();
|
||||
|
||||
// 验证版本号是否为7
|
||||
assertEquals(7, uuid.version());
|
||||
|
||||
// 验证变体是否为IETF variant
|
||||
assertEquals(2, uuid.variant());
|
||||
|
||||
}
|
||||
|
||||
@RepeatedTest(10)
|
||||
public void testUUIDv7Uniqueness() {
|
||||
Set<org.dromara.hutool.core.data.id.UUID> uuids = new HashSet<>();
|
||||
|
||||
// 生成100万个UUIDv7,验证是否有重复
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
org.dromara.hutool.core.data.id.UUID uuid = org.dromara.hutool.core.data.id.UUID.randomUUID7();
|
||||
assertFalse(uuids.contains(uuid));
|
||||
uuids.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUUIDv7Monotonicity() {
|
||||
org.dromara.hutool.core.data.id.UUID prev = org.dromara.hutool.core.data.id.UUID.randomUUID7();
|
||||
|
||||
// 验证连续生成的1000个UUIDv7是否呈单调递增趋势
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
org.dromara.hutool.core.data.id.UUID next = org.dromara.hutool.core.data.id.UUID.randomUUID7();
|
||||
assertTrue(next.compareTo(prev) > 0);
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UUIDv7的性能测试
|
||||
*/
|
||||
@Test
|
||||
public void uuidv7BenchTest() {
|
||||
final StopWatch timer = DateUtil.createStopWatch();
|
||||
|
||||
// UUID v7 generation benchmark
|
||||
timer.start("UUID v7 generation");
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
IdUtil.randomUUID7();
|
||||
}
|
||||
timer.stop();
|
||||
Console.log("UUIDv7 generation time: {} ms", timer.getLastTaskTimeMillis());
|
||||
|
||||
|
||||
timer.start("UUID v7 generation and formatting");
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
IdUtil.randomUUID7().replace("-", "");
|
||||
}
|
||||
timer.stop();
|
||||
Console.log("UUIDv7 generation and formatting time: {} ms", timer.getLastTaskTimeMillis());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user