помещение
UUIDдаUniversally Unique IDentifierАббревиатура от , переводимая как универсальный уникальный идентификатор или глобальный уникальный идентификатор. заUUIDописание, ниже приведена выдержка из файла спецификацииA Universally Unique IDentifier (UUID) URN NamespaceНекоторые описания в:
UUID (также известные как GUID) определяют пространство имен унифицированного имени ресурса. Длина UUID составляет 128 бит, что может гарантировать уникальность в пространстве и времени.
мотивация:
Одна из основных причин использования UUID в том, что нет необходимости в централизованном управлении, один из форматов определяет идентификатор узла IEEE 802, а другие нет. UUID может автоматически генерироваться по запросу и применяться к нескольким различным сценариям. Алгоритм UUID поддерживает чрезвычайно высокие скорости выделения, каждая машина может генерировать более 10 миллионов UUID в секунду, поэтому их можно использовать в качестве идентификаторов транзакций. UUID имеет фиксированный размер 128 бит, по сравнению с другими альтернативами, он имеет преимущество небольшого размера, он очень подходит для различной сортировки, хеширования и хранения в базе данных, а также обладает характеристиками простоты использования программирования.
Просто помните здесьUUIDНесколько основных конкретных:
- глобальная пространственно-временная уникальность
- Фиксированная длина
128биты, то есть16байт(1 byte = 8 bit) - Скорость распределения чрезвычайно высока, и одна машина может генерировать более
1000десять тысячUUID(на самом деле выше)
нижеJDKсерединаUUIDПроведите подробный анализUUIDАлгоритм генерации. выбрано при написании этой статьиJDKзаJDK11.
Давайте снова поговорим об UUID
Для того, чтобы написать простое резюме, я лишь вкратце выдернул некоторые главы в файле спецификации, и подробно расскажу об этом здесь.UUIDНекоторые определения , вероятности столкновения и т.д.
определение UUID
UUIDЭто стандарт для создания программного обеспечения и часть организации Open Software Foundation в области распределенных вычислительных сред. Цель этого стандарта состоит в том, чтобы все элементы или компоненты в распределенной системе имели однозначно идентифицируемую информацию, из-за крайне низкой частоты конфликтов и основы эффективных алгоритмов не требуется централизованного контроля и управления уникально идентифицируемой информацией. при этом каждый пользователь может создавать неконфликтующие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, Microsoft рано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 |
Отметка времени низкая | 4 | 8 | представляет минимум временной метки32Целочисленное представление битов |
time_mid |
Медиана временной метки | 2 | 4 | представляет собой середину метки времени16Целочисленное представление битов |
time_hi_and_version |
Старшая отметка времени и номер версии | 2 | 4 | высоко4Бит - это представление номера версии, остаток - это максимум временной метки.12Целочисленное представление битов |
clock_seq_hi_and_res clock_seq_low |
Последовательность часов и нумерация вариантов | 2 | 4 | старший бит1прибыть3Биты представляют номер варианта, остальные13прибыть15Биты представляют последовательность часов |
node |
Идентификатор узла | 6 | 12 |
48Идентификатор узла в битах |
Нарисуйте график на основе этой таблицы:
Серьезное внимание, повторите три раза:
- упомянутый выше
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вероятность столкновения? Это математическая задача, которую можно решить с помощью более известногопарадокс дня рождениярешить:
Картинка выше взята из энциклопедии поисковой системы. Бывает, что в Википедии приведен процесс расчета вероятности столкновения, на самом деле используется и метод расчета парадокса дня рождения, вставьте его сюда:
Приведенный выше расчет вероятности столкновения основан наLeach–SalzВарианты и версии4выполнено, вывод такой:
-
103триллионUUIDВероятность найти дубликат в 1 на миллиард - Чтобы создать уровень конфликта, достигающий
50%изUUIDКак минимум нужно сгенерировать2.71 * 1_000_000^3индивидуальныйUUID
Не беспокойтесь о своей жизниUUIDВероятность конфликта ниже, чем вероятность падения большого метеорита на землю.
Сценарии использования UUID
Практически все сценарии, требующие использования глобально уникальных идентификаторов, могут быть использованы.UUID, если нет явного ограничения на длину, распространенные сценарии включают:
- Структура ведения журналов отображает карты в контексте диагностики
TRACE_ID -
APMинструмент илиOpenTracingв спецификацииSPAN_ID - Первичный ключ базы данных или виртуальный внешний ключ в особых случаях
- торговля
ID(ЗаказID) - так далее......
Подробное введение и использование UUID в JDK
Вот как это использовать. упомянутый ранее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
Изучите реализацию исходного кода UUID в JDK
java.util.UUIDодеялоfinalмодифицированный, реализованныйSerializableа такжеComparableИнтерфейсы, в общем понимании, имеют следующие особенности:
- Неизменяемый, обычно классы инструментов определяются следующим образом
- Сериализуемый и десериализуемый
- Различные объекты можно сравнивать, и метод сравнения будет проанализирован позже.
Далее будет проанализировано с разных сторонjava.util.UUIDРеализация исходного кода:
- Свойства и конструкторы
- Реализация версии случайных чисел
- реализация версии 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_low |
32 | 8 |
time_mid |
16 | 4 |
version |
4 | 1 |
time_hi |
12 | 3 |
Низкий64битыleastSigBitsМакет
| поле |
bitдлина |
16длина базового символа |
|---|---|---|
variant |
2 | меньше 1 |
clock_seq |
14 |
variantа такжеclock_seqв сумме 4 |
node |
48 | 12 |
см. далее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 */
// 清空变体号所在的位,重新设置为2
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Атрибуты используются для указания источника случайности системы по умолчанию:
Вот небольшая точка знаний.Если вы хотите получить безопасное случайное число в криптографическом смысле, вы можете напрямую использовать случайное число, сгенерированное генератором истинных случайных чисел, или использовать случайное число, сгенерированное генератором истинных случайных чисел, в качестве семя. Узнайте, найдя некоторую информациюНефизический генератор истинных случайных чиселимеют:
-
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середина
реализация версии 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 dash1 = name.indexOf('-', 0);
int dash2 = name.indexOf('-', dash1 + 1);
int dash3 = name.indexOf('-', dash2 + 1);
int dash4 = name.indexOf('-', dash3 + 1);
int dash5 = name.indexOf('-', dash4 + 1);
if (dash4 < 0 || dash5 >= 0) {
throw new IllegalArgumentException("Invalid UUID string: " + name);
}
long mostSigBits = Long.parseLong(name, 0, dash1, 16) & 0xffffffffL;
mostSigBits <<= 16;
mostSigBits |= Long.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL;
mostSigBits <<= 16;
mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL;
long leastSigBits = Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL;
leastSigBits <<= 48;
leastSigBits |= Long.parseLong(name, dash4 + 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);
}
↓↓↓↓↓↓↓↓↓↓↓↓
// java.lang.System
private static void setJavaLangAccess() {
SharedSecrets.setJavaLangAccess(new JavaLangAccess() {
public String fastUUID(long lsb, long msb) {
return Long.fastUUID(lsb, msb);
}
}
↓↓↓↓↓↓↓↓↓↓↓↓
// 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Вся информация об экземпляре.
резюме
На протяженииUUIDреализации исходного кода, вы обнаружите, что в дополнение к некоторым деликатным битовым операциям его реализация зависит от некоторых уже готовых функций, в том числеMD5переварить алгоритм иSecureRandomИспользование случайного источника системы для генерации безопасных случайных чисел.UUIDПричина, по которой он может стать стандартом, заключается в том, что он объединяет достижения предшественников в компьютерной области за многие годы, поэтому теперь пользователи могут писать какHello Worldтак же просто, как позвонитьUUID.randomUUID().
Использованная литература:
- RFC 4122
- Википедия — универсальный уникальный идентификатор
- Исходный код, связанный с JDK11
Открытый вопрос к читателю:
-
UUIDКакие функции используются для минимизации конфликтности? - Человек может размножаться
UUIDВсе израсходовано?
(Конец этой статьи c-2-w e-a-20210129)