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-05Java?Stream.reduce()用法詳細(xì)解析
Stream API提供了豐富的中間函數(shù),歸并函數(shù)和終端函數(shù),這些函數(shù)還支持并行化執(zhí)行,下面這篇文章主要給大家介紹了關(guān)于Java?Stream.reduce()用法的相關(guān)資料,需要的朋友可以參考下2022-12-12springmvc接口接收參數(shù)與請(qǐng)求參數(shù)格式的整理
這篇文章主要介紹了springmvc接口接收參數(shù)與請(qǐng)求參數(shù)格式的整理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Java實(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-03Java經(jīng)典排序算法之插入排序代碼實(shí)例
這篇文章主要介紹了Java經(jīng)典排序算法之插入排序代碼實(shí)例,插入排序是一種最簡(jiǎn)單直觀的排序算法,它的工作原理是通過構(gòu)建有序序列,對(duì)于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入,需要的朋友可以參考下2023-10-10