Java生成唯一ID的三種方法總結(jié)
使用AtomicLong生成唯一ID(適用于單機(jī)場(chǎng)景)
這個(gè)示例已經(jīng)在之前的回答中給出,但我會(huì)再次展示它,以便與后續(xù)示例保持連貫性。
import java.util.concurrent.atomic.AtomicLong;
public class UniqueIdGenerator {
private final AtomicLong counter = new AtomicLong(0);
public long nextId() {
return counter.incrementAndGet();
}
public static void main(String[] args) {
UniqueIdGenerator generator = new UniqueIdGenerator();
long id1 = generator.nextId();
long id2 = generator.nextId();
System.out.println("ID 1: " + id1);
System.out.println("ID 2: " + id2);
}
}使用UUID并轉(zhuǎn)換為數(shù)字形式(雖然不是純數(shù)字,但提供了唯一性)
由于UUID本身是字符串形式的,我們可以通過哈希函數(shù)(如MD5、SHA-1等)將其轉(zhuǎn)換為數(shù)字,但需要注意哈希碰撞的可能性(盡管在實(shí)際應(yīng)用中非常低)。然而,更常見的是保留UUID的字符串形式或使用其作為基礎(chǔ)來生成符合特定需求的數(shù)字ID。
import java.util.UUID;
public class UuidToNumberExample {
// 注意:這不是將UUID直接轉(zhuǎn)換為唯一數(shù)字的有效方法,因?yàn)榇嬖诠E鲎驳娘L(fēng)險(xiǎn)。
// 這里只是為了說明概念。
public static long uuidToNumber(UUID uuid) {
// 簡(jiǎn)單的示例:將UUID的字符串表示形式與另一個(gè)數(shù)字組合,然后進(jìn)行哈希(注意:這不是安全的做法)
String uuidStr = uuid.toString();
long base = 123456789L; // 假設(shè)的基數(shù)
// 這里不實(shí)現(xiàn)哈希函數(shù),而是使用字符串長(zhǎng)度加基數(shù)作為示例(僅為演示)
return uuidStr.length() + base; // 這不是一個(gè)好的實(shí)現(xiàn)!
// 更實(shí)際的方法是使用安全的哈希函數(shù),但請(qǐng)注意哈希碰撞的可能性
// 并且,由于哈希函數(shù)的輸出是固定長(zhǎng)度的,直接用作ID可能需要進(jìn)一步處理
}
public static void main(String[] args) {
UUID uuid = UUID.randomUUID();
long number = uuidToNumber(uuid); // 這里的實(shí)現(xiàn)是錯(cuò)誤的,僅用于說明
System.out.println("UUID: " + uuid);
System.out.println("Converted Number (Incorrect Method): " + number);
// 正確的做法可能是將UUID用作查找或生成更復(fù)雜ID的基礎(chǔ)
}
}模擬Snowflake算法生成唯一ID(分布式場(chǎng)景)
Snowflake算法的實(shí)現(xiàn)相對(duì)復(fù)雜,但我們可以簡(jiǎn)化其核心思想來展示一個(gè)基本框架。
public class SnowflakeIdWorker {
// 假設(shè)的時(shí)間戳、數(shù)據(jù)中心ID、機(jī)器ID和序列號(hào)字段(實(shí)際應(yīng)用中需要更精細(xì)的管理)
private final long twepoch = 1288834974657L; // 自定義起始時(shí)間戳
private final long datacenterIdBits = 5L; // 數(shù)據(jù)中心ID位數(shù)
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 數(shù)據(jù)中心ID最大值
private final long workerIdBits = 5L; // 機(jī)器ID位數(shù)
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 機(jī)器ID最大值
private final long sequenceBits = 12L; // 序列號(hào)位數(shù)
private final long workerIdShift = sequenceBits; // 機(jī)器ID左移位數(shù)
private final long datacenterIdShift = sequenceBits + workerIdBits; // 數(shù)據(jù)中心ID左移位數(shù)
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 時(shí)間戳左移位數(shù)
private long sequence = 0L; // 序列號(hào)
private long lastTimestamp = -1L; // 上次生成ID的時(shí)間戳
// 構(gòu)造函數(shù)中初始化workerId和datacenterId(這里為示例,實(shí)際中應(yīng)由外部指定)
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
// 這里省略了實(shí)際將workerId和datacenterId設(shè)置到對(duì)象中的步驟
}
// 生成ID的方法(簡(jiǎn)化版,未包含時(shí)鐘回?fù)芴幚淼龋?
public synchronized long nextId() {
long timestamp = timeGen();
// 如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳,說明系統(tǒng)時(shí)鐘回?fù)苓^
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
// 如果是同一時(shí)間生成的,則進(jìn)行毫秒內(nèi)序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & (-1L ^ (-1L << sequenceBits));
if (sequence == 0) {
// 毫秒內(nèi)序列溢出
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 時(shí)間戳改變,毫秒內(nèi)序列重置
sequence = 0L;
}
// 上次生成ID的時(shí)間戳
lastTimestamp = timestamp;
// 移位并通過或運(yùn)算拼到一起組成64位的ID
// 這里省略了實(shí)際的workerId和datacenterId的左移和或操作
// 示例:return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
return 0L; // 返回模擬的0,實(shí)際應(yīng)返回通過上述方式計(jì)算的ID
}
// 模擬獲取系統(tǒng)當(dāng)前時(shí)間戳(毫秒)
protected long timeGen() {
return System.currentTimeMillis();
}
// 等待到下一個(gè)毫秒,獲得新的時(shí)間戳
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
// 省略main方法和其他可能的輔助方法...
}請(qǐng)注意,上述Snowflake算法的實(shí)現(xiàn)是高度簡(jiǎn)化的,并省略了許多關(guān)鍵細(xì)節(jié)(如實(shí)際的數(shù)據(jù)中心ID和機(jī)器ID的設(shè)置、序列號(hào)的正確處理以及時(shí)鐘回?fù)艿脑敿?xì)處理邏輯)。在實(shí)際應(yīng)用中,您需要根據(jù)自己的需求和環(huán)境來完整實(shí)現(xiàn)這些功能。
總結(jié)
在單機(jī)環(huán)境下,可以使用AtomicLong來生成唯一ID;而在需要非純數(shù)字形式的場(chǎng)景中,可以通過UUID結(jié)合哈希函數(shù)如MD5或SHA-1轉(zhuǎn)換成數(shù)字,但需注意哈希碰撞的低概率風(fēng)險(xiǎn);對(duì)于分布式系統(tǒng),模擬Snowflake算法是一種復(fù)雜但有效的方法,每種方法都有其適用場(chǎng)景和潛在問題,需要根據(jù)具體需求和環(huán)境選擇合適的實(shí)現(xiàn)方式。
到此這篇關(guān)于Java生成唯一ID的三種方法總結(jié)的文章就介紹到這了,更多相關(guān)Java生成唯一ID內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis不支持batchInsertOrUpdate返顯id問題
這篇文章主要介紹了Mybatis不支持batchInsertOrUpdate返顯id問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
使用@DS輕松解決動(dòng)態(tài)數(shù)據(jù)源的問題
這篇文章主要介紹了使用@DS輕松解決動(dòng)態(tài)數(shù)據(jù)源的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Java?Stream.reduce()用法詳細(xì)解析
Stream API提供了豐富的中間函數(shù),歸并函數(shù)和終端函數(shù),這些函數(shù)還支持并行化執(zhí)行,下面這篇文章主要給大家介紹了關(guān)于Java?Stream.reduce()用法的相關(guān)資料,需要的朋友可以參考下2022-12-12
springmvc接口接收參數(shù)與請(qǐng)求參數(shù)格式的整理
這篇文章主要介紹了springmvc接口接收參數(shù)與請(qǐng)求參數(shù)格式的整理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Java實(shí)現(xiàn)調(diào)用jython執(zhí)行python文件的方法
這篇文章主要介紹了Java實(shí)現(xiàn)調(diào)用jython執(zhí)行python文件的方法,結(jié)合實(shí)例形式分析了Java調(diào)用jython執(zhí)行python文件的常見操作技巧及相關(guān)問題解決方法,需要的朋友可以參考下2018-03-03
Java經(jīng)典排序算法之插入排序代碼實(shí)例
這篇文章主要介紹了Java經(jīng)典排序算法之插入排序代碼實(shí)例,插入排序是一種最簡(jiǎn)單直觀的排序算法,它的工作原理是通過構(gòu)建有序序列,對(duì)于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入,需要的朋友可以參考下2023-10-10

