91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JDK中UUID的底層實現方式

發布時間:2021-09-18 15:44:07 來源:億速云 閱讀:149 作者:chen 欄目:web開發

這篇文章主要介紹“JDK中UUID的底層實現方式”,在日常操作中,相信很多人在JDK中UUID的底層實現方式問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”JDK中UUID的底層實現方式”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

前提

UUID是Universally Unique  IDentifier的縮寫,翻譯為通用唯一標識符或者全局唯一標識符。對于UUID的描述,下面摘錄一下規范文件A Universally Unique  IDentifier (UUID) URN Namespace中的一些描述:

UUID(也稱為GUID)定義了統一資源名稱命名空間。UUID的長度為128比特,可以保證在空間和時間上的唯一性。

「動機:」

使用UUID的主要原因之一是不需要集中式管理,其中一種格式限定了IEEE  802節點標識符,其他格式無此限制。可以自動化按需生成UUID,應用于多重不同的場景。UUID算法支持極高的分配速率,每臺機器每秒鐘可以生成超過1000萬個UUID,因此它們可以作為事務ID使用。UUID具有固定大小128比特,與其他替代方案相比,它具有體積小的優勢,非常適用于各種排序、散列和存儲在數據庫中,具有編程易用性的特點。

這里只需要記住UUID幾個核心特定:

  • 全局時空唯一性

  • 固定長度128比特,也就是16字節(1 byte = 8 bit)

  • 分配速率極高,單機每秒可以生成超過1000萬個UUID(實際上更高)

下面就JDK中的UUID實現詳細分析一下UUID生成算法。編寫本文的時候選用的JDK為JDK11。

再聊UUID

前面為了編寫簡單的摘要,所以只粗略摘錄了規范文件里面的一些章節,這里再詳細聊聊UUID的一些定義、碰撞概率等等。

UUID定義

UUID是一種軟件構建的標準,也是開放軟件基金會組織在分布式計算環境領域的一部分。提出此標準的目的是:讓分布式系統中的所有元素或者組件都有唯一的可辨別的信息,因為極低沖突頻率和高效算法的基礎,它不需要集中式控制和管理唯一可辨別信息的生成,由此,每個使用者都可以自由地創建與其他人不沖突的UUID。

「UUID本質是一個128比特的數字」,這是一個位長巨大的數值,理論上來說,UUID的總數量為2^128個。這個數字大概可以這樣估算:如果「每納秒」產生「1兆」個不相同的UUID,需要花費超過100億年才會用完所有的UUID。

UUID的變體與版本

UUID標準和算法定義的時候,為了考慮歷史兼容性和未來的擴展,提供了多種變體和版本。接下來的變體和版本描述來源于維基百科中的Versions章節和RFC  4122中的Variant章節。

目前已知的變體如下:

  • 變體0xx:Reserved, NCS backward compatibility,為向后兼容做預留的變體

  • 變體10x:The IETF aka Leach-Salz variant (used by this class),稱為Leach–Salz  UUID或者IETF UUID,JDK中UUID目前正在使用的變體

  • 變體110:Reserved, Microsoft Corporation backward compatibility,微軟早期GUID預留變體

  • 變體111:Reserved for future definition,將來擴展預留,目前還沒被使用的變體

目前已知的版本如下:

  • 空UUID(特殊版本0),用00000000-0000-0000-0000-000000000000表示,也就是所有的比特都是0

  • date-time and MAC  address(版本1):基于時間和MAC地址的版本,通過計算當前時間戳、隨機數和機器MAC地址得到。由于有MAC地址,這個可以保證其在全球的唯一性。但是使用了MAC地址,就會有MAC地址暴露問題。若是局域網,可以用IP地址代替

  • date-time and MAC address, DCE security  version(版本2):分布式計算環境安全的UUID,算法和版本1基本一致,但會把時間戳的前4位置換為POSIX的UID或GID

  • namespace name-based  MD5(版本3):通過計算名字和命名空間的MD5散列值得到。這個版本的UUID保證了:相同命名空間中不同名字生成的UUID的唯一性;不同命名空間中的UUID的唯一性;相同命名空間中相同名字的UUID重復生成是相同的

  • random(版本4):根據隨機數,或者偽隨機數生成UUID。這種UUID產生重復的概率是可以計算出來的,還有一個特點就是預留了6比特存放變體和版本屬性,所以隨機生成的位一共有122個,總量為2^122,比其他變體的總量要偏少

  • namespace name-based SHA-1(版本5):和版本3類似,散列算法換成了SHA-1

其中,JDK中應用的變體是Leach-Salz,提供了namespace name-based  MD5(版本3)和random(版本4)兩個版本的UUID生成實現。

UUID的格式

在規范文件描述中,UUID是由16個8比特數字,或者說32個16進制表示形式下的字符組成,一般表示形式為8-4-4-4-12,加上連接字符-一共有36個字符,例如:

## 例子 123e4567-e89b-12d3-a456-426614174000 ## 通用格式 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

其中4比特長度的M和1到3比特長度的N分別代表版本號和變體標識。UUID的具體布局如下:

屬性屬性名長度(bytes長度(16進制字符)內容
time_low時間戳低位48代表時間戳的低32比特的整數表示
time_mid時間戳中位24代表時間戳的中間16比特的整數表示
time_hi_and_version時間戳高位和版本號24高位4比特是版本號表示,剩余是時間戳的高12比特的整數表示
clock_seq_hi_and_res clock_seq_low時鐘序列與變體編號24最高位13比特表示變體編號,剩下的1315比特表示時鐘序列
node節點ID61248比特表示的節點ID

基于這個表格畫一個圖:

JDK中UUID的底層實現方式

「嚴重注意,重復三次」:

  • 上面提到的UUID的具體布局只適用于date-time and MAC address(版本1)和date-time and MAC address,  DCE security version(版本2),其他版本雖然采用了基本一樣的字段分布,但是無法獲取時間戳、時鐘序列或者節點ID等信息

  • 上面提到的UUID的具體布局只適用于date-time and MAC address(版本1)和date-time and MAC address,  DCE security version(版本2),其他版本雖然采用了基本一樣的字段分布,但是無法獲取時間戳、時鐘序列或者節點ID等信息

  • 上面提到的UUID的具體布局只適用于date-time and MAC address(版本1)和date-time and MAC address,  DCE security version(版本2),其他版本雖然采用了基本一樣的字段分布,但是無法獲取時間戳、時鐘序列或者節點ID等信息

JDK中只提供了版本3和版本4的實現,但是java.util.UUID的布局采用了上面表格的字段

UUID的碰撞幾率計算

UUID的總量雖然巨大,但是如果不停地使用,假設每納秒生成超過1兆個UUID并且人類有幸能夠繁衍到100億年以后,總會有可能產生重復的UUID。那么,怎么計算UUID的碰撞幾率呢?這是一個數學問題,可以使用比較著名的「生日悖論」解決:

JDK中UUID的底層實現方式

上圖來源于某搜索引擎百科。剛好維基百科上給出了碰撞幾率的計算過程,其實用的也是生日悖論的計算方法,這里貼一下:

JDK中UUID的底層實現方式

上面的碰撞幾率計算是基于Leach–Salz變體和版本4進行,得到的結論是:

  • 103萬億個UUID中找到重復項的概率是十億分之一

  • 要生成一個沖突率達到50%的UUID至少需要生成2.71 * 1_000_000^3個UUID

有生之年不需要擔心UUID沖突,出現的可能性比大型隕石撞地球還低。

JDK中UUID的底層實現方式

UUID的使用場景

基本所有需要使用全局唯一標識符的場景都可以使用UUID,除非對長度有明確的限制,常用的場景包括:

  • 日志框架映射診斷上下文中的TRACE_ID

  • APM工具或者說OpenTracing規范中的SPAN_ID

  • 特殊場景下數據庫主鍵或者虛擬外鍵

  • 交易ID(訂單ID)

  • 等等......

JDK中UUID詳細介紹和使用

這里先介紹使用方式。前面提到JDK中應用的變體是Leach-Salz(變體2),提供了namespace name-based  MD5(版本3)和random(版本4)兩個版本的UUID生成實現,實際上java.util.UUID提供了四種生成UUID實例的方式:

  • 最常見的就是調用靜態方法UUID#randomUUID(),這就是版本4的靜態工廠方法

  • 其次是調用靜態方法UUID#nameUUIDFromBytes(byte[] name),這就是版本3的靜態工廠方法

  • 另外有調用靜態方法UUID#fromString(String name),這是解析8-4-4-4-12格式字符串生成UUID實例的靜態工廠方法

  • 還有低層次的構造函數UUID(long mostSigBits, long leastSigBits),這個對于使用者來說并不常見

最常用的方法有實例方法toString(),把UUID轉化為16進制字符串拼接而成的8-4-4-4-12形式表示,例如:

String uuid = UUID.randomUUID().toString();

其他Getter方法:

UUID uuid = UUID.randomUUID(); // 返回版本號 int version = uuid.version(); // 返回變體號 int variant = uuid.variant(); // 返回時間戳 - 這個方法會報錯,只有Time-based UUID也就是版本1或者2的UUID實現才能返回時間戳 long timestamp = uuid.timestamp(); // 返回時鐘序列 - 這個方法會報錯,只有Time-based UUID也就是版本1或者2的UUID實現才能返回時鐘序列 long clockSequence = uuid.clockSequence(); // 返回節點ID - 這個方法會報錯,只有Time-based UUID也就是版本1或者2的UUID實現才能返回節點ID long nodeId = uuid.node();

可以驗證一下不同靜態工廠方法的版本和變體號:

UUID uuid = UUID.randomUUID(); int version = uuid.version(); int variant = uuid.variant(); System.out.println(String.format("version:%d,variant:%d", version, variant)); uuid = UUID.nameUUIDFromBytes(new byte[0]); version = uuid.version(); variant = uuid.variant(); System.out.println(String.format("version:%d,variant:%d", version, variant)); // 輸出結果 version:4,variant:2 version:3,variant:2

探究JDK中UUID源碼實現

java.util.UUID被final修飾,實現了Serializable和Comparable接口,從一般理解上看,有下面的特定:

  • 不可變,一般來說工具類都是這樣定義的

  • 可序列化和反序列化

  • 不同的對象之間可以進行比較,比較方法后面會分析

下面會從不同的方面分析一下java.util.UUID的源碼實現:

  • 屬性和構造函數

  • 隨機數版本實現

  • namespace name-based MD5版本實現

  • 其他實現

  • 格式化輸出

  • 比較相關的方法

屬性和構造函數

前面反復提到JDK中只提供了版本3和版本4的實現,但是java.util.UUID的布局采用了UUID規范中的字段定義,長度一共128比特,剛好可以存放在兩個long類型的整數中,所以看到了UUID類中存在兩個long類型的整型數值:

public final class UUID implements java.io.Serializable, Comparable<UUID> {         // 暫時省略其他代碼      /*      * The most significant 64 bits of this UUID.      * UUID中有效的高64比特      *      * @serial      */     private final long mostSigBits;      /*      * The least significant 64 bits of this UUID.      *  UUID中有效的低64比特      *      * @serial      */     private final long leastSigBits;          // 暫時省略其他代碼 }

從UUID類注釋中可以看到具體的字段布局如下:

「高64比特mostSigBits的布局」

字段bit長度16進制字符長度
time_low328
time_mid164
version41
time_hi123

「低64比特leastSigBits的布局」

字段bit長度16進制字符長度
variant2小于1
clock_seq14variantclock_seq加起來等于4
node4812

接著看UUID的其他成員屬性和構造函數:

public final class UUID implements java.io.Serializable, Comparable<UUID> {         // 暫時省略其他代碼          // Java語言訪問類,里面存放了很多底層相關的訪問或者轉換方法,在UUID中主要是toString()實例方法用來格式化成8-4-4-4-12的形式,委托到Long.fastUUID()方法     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();      // 靜態內部類確保SecureRandom初始化,用于版本4的隨機數UUID版本生成安全隨機數     private static class Holder {         static final SecureRandom numberGenerator = new SecureRandom();     }          // 通過長度為16的字節數組,計算mostSigBits和leastSigBits的值初始化UUID實例     private UUID(byte[] data) {         long msb = 0;         long lsb = 0;         assert data.length == 16 : "data must be 16 bytes in length";         for (int i=0; i<8; i++)             msb = (msb << 8) | (data[i] & 0xff);         for (int i=8; i<16; i++)             lsb = (lsb << 8) | (data[i] & 0xff);         this.mostSigBits = msb;         this.leastSigBits = lsb;     }          // 直接指定mostSigBits和leastSigBits構造UUID實例     public UUID(long mostSigBits, long leastSigBits) {         this.mostSigBits = mostSigBits;         this.leastSigBits = leastSigBits;     }      // 暫時省略其他代碼 }

私有構造private UUID(byte[] data)中有一些位運算技巧:

long msb = 0; long lsb = 0; assert data.length == 16 : "data must be 16 bytes in length"; for (int i=0; i<8; i++)     msb = (msb << 8) | (data[i] & 0xff); for (int i=8; i<16; i++)     lsb = (lsb << 8) | (data[i] & 0xff); this.mostSigBits = msb; this.leastSigBits = lsb;

輸入的字節數組長度為16,mostSigBits由字節數組的前8個字節轉換而來,而leastSigBits由字節數組的后8個字節轉換而來。中間變量msb或者lsb在提取字節位進行計算的時候:

  • 先進行左移8位確保需要計算的位為0,已經計算好的位移動到左邊

  • 然后右邊需要提取的字節data[i]的8位會先和0xff(補碼1111  1111)進行或運算,確保不足8位的高位被補充為0,超過8位的高位會被截斷為低8位,也就是data[i] & 0xff確保得到的補碼為8位

  • 前面兩步的結果再進行或運算

一個模擬過程如下:

(為了區分明顯,筆者每4位加了一個下劃線)  (為了簡答,只看字節數組的前4個字節,同時只看long類型的前4個字節)  0xff === 1111_1111  long msb = 0  => 0000_0000 0000_0000 0000_0000 0000_0000  byte[] data 0000_0001 0000_0010 0000_0100 0000_1000  i = 0(第一輪) msb << 8 = 0000_0000 0000_0000 0000_0000 0000_0000 data[i] & 0xff = 0000_0001 & 1111_1111 = 0000_0001 (msb << 8) | (data[i] & 0xff) = 0000_0000 0000_0000 0000_0000 0000_0001  (第一輪 msb = 0000_0000 0000_0000 0000_0000 0000_0001)  i = 1(第二輪) msb << 8 = 0000_0000 0000_0000 0000_0001 0000_0000 data[i] & 0xff = 0000_0010 & 1111_1111 = 0000_0010 (msb << 8) | (data[i] & 0xff) = 0000_0000 0000_0000 0000_0001 0000_0010  (第二輪 msb = 0000_0000 0000_0000 0000_0001 0000_0010)  i = 2(第三輪) msb << 8 = 0000_0000 0000_0001 0000_0010 0000_0000 data[i] & 0xff = 0000_0100 & 1111_1111 = 0000_0100 (msb << 8) | (data[i] & 0xff) = 0000_0000 0000_0001 0000_0010 0000_0100  (第三輪 msb = 0000_0000 0000_0001 0000_0010 0000_0100)  i = 3(第四輪) msb << 8 = 0000_0001 0000_0010 0000_0100 0000000 data[i] & 0xff = 0000_1000 & 1111_1111 = 0000_1000 (msb << 8) | (data[i] & 0xff) = 0000_0001 0000_0010 0000_0100 0000_1000  (第四輪 msb = 0000_0001 0000_0010 0000_0100 0000_1000)

以此類推,這個私有構造函數執行完畢后,長度為16的字節數組的所有位就會轉移到mostSigBits和leastSigBits中。

隨機數版本實現

構造函數分析完,接著分析重磅的靜態工廠方法UUID#randomUUID(),這是使用頻率最高的一個方法:

public static UUID randomUUID() {     // 靜態內部類Holder持有的SecureRandom實例,確保提前初始化     SecureRandom ng = Holder.numberGenerator;     // 生成一個16字節的安全隨機數,放在長度為16的字節數組中     byte[] randomBytes = new byte[16];     ng.nextBytes(randomBytes);     // 清空版本號所在的位,重新設置為4     randomBytes[6]  &= 0x0f;  /* clear version        */     randomBytes[6]  |= 0x40;  /* set to version 4     */     // 清空變體號所在的位,重新設置為     randomBytes[8]  &= 0x3f;  /* clear variant        */     randomBytes[8]  |= 0x80;  /* set to IETF variant  */     return new UUID(randomBytes); }

關于上面的位運算,這里可以使用極端的例子進行推演:

假設randomBytes[6] = 1111_1111 // 清空version位 randomBytes[6] &= 0x0f => 1111_1111 & 0000_1111 = 0000_1111 得到randomBytes[6] = 0000_1111 (這里可見高4比特被清空為0) // 設置version位為整數4 => 十六進制0x40 => 二級制補碼0100_0000 randomBytes[6] |= 0x40 => 0000_1111 | 0100_0000 = 0100_1111 得到randomBytes[6] = 0100_1111  結果:version位 => 0100(4 bit)=> 對應十進制數4  同理  假設randomBytes[8] = 1111_1111 // 清空variant位 randomBytes[8] &= 0x3f => 1111_1111 & 0011_1111 = 0011_1111 // 設置variant位為整數128 => 十六進制0x80 => 二級制補碼1000_0000 (這里取左邊高位2位) randomBytes[8] |= 0x80 => 0011_1111 | 1000_0000 = 1011_1111  結果:variant位 => 10(2 bit)=> 對應十進制數2

關于UUID里面的Getter方法例如version()、variant()其實就是找到對應的位,并且轉換為十進制整數返回,如果熟練使用位運算,應該不難理解,后面不會分析這類的Getter方法。

「隨機數版本實現強依賴于SecureRandom生成的隨機數(字節數組)」。SecureRandom的引擎提供者可以從sun.security.provider.SunEntries中查看,對于不同系統版本的JDK實現會選用不同的引擎,常見的如NativePRNG。JDK11配置文件$JAVA_HOME/conf/security/java.security中的securerandom.source屬性用于指定系統默認的隨機源:

JDK中UUID的底層實現方式

這里要提一個小知識點,想要得到密碼學意義上的安全隨機數,可以直接使用真隨機數產生器產生的隨機數,或者使用真隨機數產生器產生的隨機數做種子。通過查找一些資料得知「非物理真隨機數產生器」有:

  • Linux操作系統的/dev/random設備接口

  • Windows操作系統的CryptGenRandom接口

如果不修改java.security配置文件,默認隨機數提供引擎會根據不同的操作系統選用不同的實現,這里不進行深究。在Linux環境下,SecureRandom實例化后,不通過setSeed()方法設置隨機數作為種子,默認就是使用/dev/random提供的安全隨機數接口獲取種子,產生的隨機數是密碼學意義上的安全隨機數。「一句話概括,UUID中的私有靜態內部類Holder中的SecureRandom實例可以產生安全隨機數,這個是JDK實現UUID版本4的一個重要前提」。這里總結一下隨機數版本UUID的實現步驟:

  • 通過SecureRandom依賴提供的安全隨機數接口獲取種子,生成一個16字節的隨機數(字節數組)

  • 對于生成的隨機數,清空和重新設置version和variant對應的位

  • 把重置完version和variant的隨機數的所有位轉移到mostSigBits和leastSigBits中

namespace name-based MD5版本實現

接著分析版本3也就是namespace name-based  MD5版本的實現,對應于靜態工廠方法UUID#nameUUIDFromBytes():

public static UUID nameUUIDFromBytes(byte[] name) {     MessageDigest md;     try {         md = MessageDigest.getInstance("MD5");     } catch (NoSuchAlgorithmException nsae) {         throw new InternalError("MD5 not supported", nsae);     }     byte[] md5Bytes = md.digest(name);     md5Bytes[6]  &= 0x0f;  /* clear version        */     md5Bytes[6]  |= 0x30;  /* set to version 3     */     md5Bytes[8]  &= 0x3f;  /* clear variant        */     md5Bytes[8]  |= 0x80;  /* set to IETF variant  */     return new UUID(md5Bytes); }

它的后續基本處理和隨機數版本基本一致(清空版本位的時候,重新設置為3),唯一明顯不同的地方就是生成原始隨機數的時候,采用的方式是:基于輸入的name字節數組,通過MD5摘要算法生成一個MD5摘要字節數組作為原始安全隨機數,返回的這個隨機數剛好也是16字節長度的。使用方式很簡單:

UUID uuid = UUID.nameUUIDFromBytes("throwable".getBytes());

namespace name-based MD5版本UUID的實現步驟如下:

  • 通過輸入的命名字節數組基于MD5算法生成一個16字節長度的隨機數

  • 對于生成的隨機數,清空和重新設置version和variant對應的位

  • 把重置完version和variant的隨機數的所有位轉移到mostSigBits和leastSigBits中

namespace name-based MD5版本的UUID強依賴于MD5算法,有個明顯的特征是如果輸入的byte[]  name一致的情況下,會產生完全相同的UUID實例。

其他實現

其他實現主要包括:

// 完全定制mostSigBits和leastSigBits,可以參考UUID標準字段布局進行設置,也可以按照自行制定的標準 public UUID(long mostSigBits, long leastSigBits) {     this.mostSigBits = mostSigBits;     this.leastSigBits = leastSigBits; }  // 基于字符串格式8-4-4-4-12的UUID輸入,重新解析出mostSigBits和leastSigBits,這個靜態工廠方法也不常用,里面的位運算也不進行詳細探究 public static UUID fromString(String name) {     int len = name.length();     if (len > 36) {         throw new IllegalArgumentException("UUID string too large");     }     int dash2 = name.indexOf('-', 0);     int dash3 = name.indexOf('-', dash2 + 1);     int dash4 = name.indexOf('-', dash3 + 1);     int dash5 = name.indexOf('-', dash4 + 1);     int dash6 = name.indexOf('-', dash5 + 1);     if (dash5 < 0 || dash6 >= 0) {         throw new IllegalArgumentException("Invalid UUID string: " + name);     }     long mostSigBits = Long.parseLong(name, 0, dash2, 16) & 0xffffffffL;     mostSigBits <<= 16;     mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL;     mostSigBits <<= 16;     mostSigBits |= Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL;     long leastSigBits = Long.parseLong(name, dash4 + 1, dash5, 16) & 0xffffL;     leastSigBits <<= 48;     leastSigBits |= Long.parseLong(name, dash5 + 1, len, 16) & 0xffffffffffffL;     return new UUID(mostSigBits, leastSigBits); }

格式化輸出

格式化輸出體現在UUID#toString()方法,這個方法會把mostSigBits和leastSigBits格式化為8-4-4-4-12的形式,這里詳細分析一下格式化的過程。首先從注釋上看格式是:

<time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>  time_low = 4 * <hexOctet> => 4個16進制8位字符 time_mid = 2 * <hexOctet> => 2個16進制8位字符 time_high_and_version = 4 * <hexOctet> => 2個16進制8位字符 variant_and_sequence = 4 * <hexOctet> => 2個16進制8位字符 node = 4 * <hexOctet> => 6個16進制8位字符  hexOctet = <hexDigit><hexDigit>(2個hexDigit) hexDigit = 0-9a-F(其實就是16進制的字符)

和前文布局分析時候的提到的內容一致。UUID#toString()方法源碼如下:

private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();   public String toString() {     return jla.fastUUID(leastSigBits, mostSigBits); }  &darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;  // java.lang.System private static void setJavaLangAccess() {      SharedSecrets.setJavaLangAccess(new JavaLangAccess() {              public String fastUUID(long lsb, long msb) {             return Long.fastUUID(lsb, msb);         } }  &darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr;&darr; // java.lang.Long static String fastUUID(long lsb, long msb) {     // COMPACT_STRINGS在String類中默認為true,所以會命中if分支     if (COMPACT_STRINGS) {         // 初始化36長度的字節數組          byte[] buf = new byte[36];         // lsb的低48位轉換為16進制格式寫入到buf中 - node => 位置[24,35]         formatUnsignedLong0(lsb,        4, buf, 24, 12);         // lsb的高16位轉換為16進制格式寫入到buf中 - variant_and_sequence  => 位置[19,22]         formatUnsignedLong0(lsb >>> 48, 4, buf, 19, 4);         // msb的低16位轉換為16進制格式寫入到buf中 - time_high_and_version => 位置[14,17]         formatUnsignedLong0(msb,        4, buf, 14, 4);          // msb的中16位轉換為16進制格式寫入到buf中 - time_mid => 位置[9,12]         formatUnsignedLong0(msb >>> 16, 4, buf, 9,  4);         // msb的高32位轉換為16進制格式寫入到buf中 - time_low => 位置[0,7]         formatUnsignedLong0(msb >>> 32, 4, buf, 0,  8);         // 空余的字節槽位插入'-',剛好占用了4個字節         buf[23] = '-';         buf[18] = '-';         buf[13] = '-';         buf[8]  = '-';         // 基于處理好的字節數組,實例化String,并且編碼指定為LATIN1         return new String(buf, LATIN1);     } else {         byte[] buf = new byte[72];         formatUnsignedLong0UTF16(lsb,        4, buf, 24, 12);         formatUnsignedLong0UTF16(lsb >>> 48, 4, buf, 19, 4);         formatUnsignedLong0UTF16(msb,        4, buf, 14, 4);         formatUnsignedLong0UTF16(msb >>> 16, 4, buf, 9,  4);         formatUnsignedLong0UTF16(msb >>> 32, 4, buf, 0,  8);         StringUTF16.putChar(buf, 23, '-');         StringUTF16.putChar(buf, 18, '-');         StringUTF16.putChar(buf, 13, '-');         StringUTF16.putChar(buf,  8, '-');         return new String(buf, UTF16);     } }  /**  * 格式化無符號的長整型,填充到字節緩沖區buf中,如果長度len超過了輸入值的ASCII格式表示,則會使用0進行填充  * 這個方法就是把輸入長整型值val,對應一段長度的位,填充到字節數組buf中,len控制寫入字符的長度,offset控制寫入buf的起始位置  * 而shift參數決定基礎格式,4是16進制,1是2進制,3是8位  */ static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) {     int charPos = offset + len;     int radix = 1 << shift;     int mask = radix - 1;     do {         buf[--charPos] = (byte)Integer.digits[((int) val) & mask];         val >>>= shift;     } while (charPos > offset); }

比較相關的方法

比較相關方法如下:

// hashCode方法基于mostSigBits和leastSigBits做異或得出一個中間變量hilo,再以32為因子進行計算 public int hashCode() {     long hilo = mostSigBits ^ leastSigBits;     return ((int)(hilo >> 32)) ^ (int) hilo; }  // equals為實例對比方法,直接對比兩個UUID的mostSigBits和leastSigBits值,完全相等的時候返回true public boolean equals(Object obj) {     if ((null == obj) || (obj.getClass() != UUID.class))         return false;     UUID id = (UUID)obj;     return (mostSigBits == id.mostSigBits &&             leastSigBits == id.leastSigBits); }  // 比較規則是mostSigBits高位大者為大,高位相等的情況下,leastSigBits大者為大 public int compareTo(UUID val) {     // The ordering is intentionally set up so that the UUIDs     // can simply be numerically compared as two numbers     return (this.mostSigBits < val.mostSigBits ? -1 :             (this.mostSigBits > val.mostSigBits ? 1 :                 (this.leastSigBits < val.leastSigBits ? -1 :                 (this.leastSigBits > val.leastSigBits ? 1 :                 0)))); }

所有比較方法僅僅和mostSigBits和leastSigBits有關,畢竟這兩個長整型就存儲了UUID實例的所有信息。

到此,關于“JDK中UUID的底層實現方式”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

农安县| 射阳县| 双柏县| 宜城市| 比如县| 崇左市| 宜兴市| 吐鲁番市| 垫江县| 娄底市| 丰镇市| 苍梧县| 浮梁县| 灌阳县| 阿巴嘎旗| 信宜市| 平乡县| 新乡市| 会同县| 甘洛县| 浪卡子县| 晋中市| 北安市| 韶山市| 新津县| 江山市| 武平县| 长治县| 竹北市| 蒲城县| 巴东县| 怀宁县| 肃南| 长子县| 白河县| 渝北区| 象山县| 西安市| 凌源市| 麻江县| 高碑店市|