Skip to content

Commit

Permalink
新增了 NanoId 的生成策略
Browse files Browse the repository at this point in the history
  • Loading branch information
blinkfox committed Mar 27, 2022
1 parent db32654 commit cb95880
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 6 deletions.
108 changes: 102 additions & 6 deletions src/main/java/com/blinkfox/fenix/id/IdWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
Expand All @@ -13,10 +14,27 @@
*/
public final class IdWorker {

private static final IdWorker defaultIdWorker = new IdWorker();

private static final int MAX_WORKER_INDEX = 0x0F;

private static final int WORKER_LENGTH = 16;

/**
* 默认生成的 ID 大小.
*
* <p>创建具有比 UUID v4 略多的唯一值的 NanoId 字符串.</p>
*/
public static final int DEFAULT_SIZE = 21;

/**
* 默认使用的字母表.
*
* <p>使用 64 个唯一符号创建对 url 友好的 NanoId 字符串.</p>
*/
private static final char[] DEFAULT_ALPHABET =
"_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

private final AtomicInteger indexCounter = new AtomicInteger(0);

private IdGenerator[] generators;
Expand Down Expand Up @@ -84,7 +102,7 @@ private int[] newSequence() {
}

/**
* 获取长度为 16 位数的 {@code long} 长整型 {@code ID} 值.
* 获取长度为 16 位数的 {@code long} 长整型雪花算法 {@code ID} 值.
*
* @return 16 位数的整型 {@code ID} 值
*/
Expand All @@ -93,7 +111,7 @@ public long getId() {
}

/**
* 获取长度为 16 位数的长整型 {@code ID} 的字符串值.
* 获取长度为 16 位数的长整型雪花算法 {@code ID} 的字符串值.
*
* @return 长整型 {@code ID} 的字符串值
*/
Expand All @@ -102,7 +120,7 @@ public String getIdString() {
}

/**
* 获取 36 进制的 {@code ID} 符串值.
* 获取 36 进制的雪花算法 {@code ID} 符串值.
*
* @return 36 进制的 {@code ID} 字符串
*/
Expand All @@ -111,7 +129,7 @@ public String get36RadixId() {
}

/**
* 根据给定的 16 位数的长整型 {@code ID},得到其对应的 36 进制的 {@code ID} 符串值.
* 根据给定的 16 位数的长整型 {@code ID},得到其对应的 36 进制的雪花算法 {@code ID} 符串值.
*
* @param id 长整型 {@code ID}
* @return 36 进制的 {@code ID} 字符串
Expand All @@ -121,7 +139,7 @@ public static String get36RadixId(long id) {
}

/**
* 获取 62 进制的 {@code ID} 符串值.
* 获取 62 进制的雪花算法 {@code ID} 符串值.
*
* @return 62 进制的 {@code ID} 字符串
*/
Expand All @@ -130,7 +148,7 @@ public String get62RadixId() {
}

/**
* 根据给定的长整型 {@code ID},得到其对应的 62 进制的 {@code ID} 符串值.
* 根据给定的长整型 {@code ID},得到其对应的 62 进制的雪花算法 {@code ID} 符串值.
*
* @param id 长整型 {@code ID}
* @return 62 进制的 {@code ID} 字符串
Expand All @@ -139,6 +157,44 @@ public static String get62RadixId(long id) {
return Radix.toString(id, Radix.RADIX_62);
}

/**
* 通过静态方法获取长度为 16 位数的 {@code long} 长整型雪花算法 {@code ID} 值.
*
* @return 16 位数的整型 {@code ID} 值
* @since v2.7.0
*/
public static long getSnowflakeId() {
return defaultIdWorker.getId();
}

/**
* 通过静态方法获取长度为 16 位数的雪花算法长整型 {@code ID} 的字符串值.
*
* @return 16 位数的整型 {@code ID} 值
* @since v2.7.0
*/
public static String getSnowflakeIdString() {
return defaultIdWorker.getIdString();
}

/**
* 通过静态方法获取 36 进制的雪花算法 {@code ID} 符串值.
*
* @return 36 进制的雪花算法 {@code ID} 字符串
*/
public static String getSnowflake36RadixId() {
return defaultIdWorker.get36RadixId();
}

/**
* 通过静态方法获取 62 进制的雪花算法 {@code ID} 符串值.
*
* @return 62 进制的雪花算法 {@code ID} 字符串
*/
public static String getSnowflake62RadixId() {
return defaultIdWorker.get62RadixId();
}

/**
* 获取长度为 32 位数的 {@code UUID} 字符串.
*
Expand All @@ -164,6 +220,46 @@ public static String get62RadixUuid() {
.toString();
}

/**
* 生成一个对 URL 优化的、伪随机的 NanoId 字符串,该 NanoId 将默认有 21 个字符.
*
* @return NanoId 字符串
*/
public static String getNanoId() {
return getNanoId(DEFAULT_SIZE);
}

/**
* 生成一个指定大小的对 URL 优化的、伪随机的 NanoId 字符串.
*
* @param size 字符串中的字符数.
* @return 一个随机的 NanoId 字符串.
*/
public static String getNanoId(final int size) {
if (size <= 0) {
throw new IllegalArgumentException("size must be greater than zero.");
}

int alphaBetaLength = DEFAULT_ALPHABET.length;
final int mask = (2 << (int) Math.floor(Math.log(alphaBetaLength - 1d) / Math.log(2))) - 1;
final int step = (int) Math.ceil(1.6 * mask * size / alphaBetaLength);

final StringBuilder builder = new StringBuilder();
while (true) {
final byte[] bytes = new byte[step];
ThreadLocalRandom.current().nextBytes(bytes);
for (int i = 0; i < step; ++i) {
final int alphabetIndex = bytes[i] & mask;
if (alphabetIndex < alphaBetaLength) {
builder.append(DEFAULT_ALPHABET[alphabetIndex]);
if (builder.length() == size) {
return builder.toString();
}
}
}
}
}

/**
* ID 生成器静态内部类.
*
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/blinkfox/fenix/id/NanoIdGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.blinkfox.fenix.id;

import java.io.Serializable;
import lombok.NoArgsConstructor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.UUIDGenerator;

/**
* Nano ID 字符串的生成器类.
*
* @author blinkfox on 2022-03-27.
* @since v2.7.0
*/
@NoArgsConstructor
public class NanoIdGenerator extends UUIDGenerator {

/**
* 用于生成 Nano ID 字符串的生成方法.
*
* @param s {@link SharedSessionContractImplementor} 实体
* @param obj 对象
* @return ID 结果
*/
@Override
public Serializable generate(SharedSessionContractImplementor s, Object obj) {
return IdWorker.getNanoId();
}

}
36 changes: 36 additions & 0 deletions src/test/java/com/blinkfox/fenix/id/IdWorkerTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.blinkfox.fenix.id;

import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;

Expand All @@ -9,6 +10,7 @@
* @author blinkfox on 2020-12-07.
* @since v2.4.0
*/
@Slf4j
public class IdWorkerTest {

private final IdWorker idWorker = new IdWorker();
Expand Down Expand Up @@ -55,6 +57,28 @@ public void get62RadixId() {
Assert.assertNotNull(idWorker.get62RadixId());
}

/**
* 测试生成的 UUID.
*/
@Test
public void getSnowflakeId() {
long id = IdWorker.getSnowflakeId();
log.info("snowflake id: [{}]", id);
Assert.assertTrue(id > 0);

String id2 = IdWorker.getSnowflakeIdString();
log.info("snowflake id string: [{}]", id2);
Assert.assertNotNull(id2);

String id3 = IdWorker.getSnowflake36RadixId();
log.info("36 radix snowflake id: [{}]", id3);
Assert.assertNotNull(id3);

String id4 = IdWorker.getSnowflake62RadixId();
log.info("62 radix snowflake id: [{}]", id4);
Assert.assertNotNull(id4);
}

/**
* 测试生成的 UUID.
*/
Expand All @@ -71,4 +95,16 @@ public void get62RadixUuid() {
Assert.assertEquals(19, IdWorker.get62RadixUuid().length());
}

@Test
public void getNanoId() {
String nanoId = IdWorker.getNanoId();
log.info("NanoId 为:【{}】.", nanoId);
Assert.assertEquals(IdWorker.DEFAULT_SIZE, nanoId.length());

int size = 15;
String nanoId2 = IdWorker.getNanoId(size);
log.info("长度为 15 的 NanoId 为:【{}】.", nanoId2);
Assert.assertEquals(size, nanoId2.length());
}

}
19 changes: 19 additions & 0 deletions src/test/java/com/blinkfox/fenix/id/NanoIdGeneratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.blinkfox.fenix.id;

import org.junit.Assert;
import org.junit.Test;

/**
* NanoIdGenerator 的单元测试类.
*
* @author blinkfox on 2020-03-27.
* @since v2.7.0
*/
public class NanoIdGeneratorTest {

@Test
public void generate() {
Assert.assertNotNull(new NanoIdGenerator().generate(null, null));
}

}

0 comments on commit cb95880

Please sign in to comment.