如何使用UUID生成唯一的Long

Sau*_*mar 29 java uuid

我需要为我的数据库主键列生成唯一的Long ID.

我以为我可以使用UUID.randomUUID().getMostSignificantBits(),但有时它会产生一些负面长,这对我来说也是个问题.

是否有可能只从UUID生成正长?会有数十亿条目,所以我希望每个生成的密钥必须是唯一的.

小智 31

UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE
Run Code Online (Sandbox Code Playgroud)

这样做的原因是,当你按位并且使用1时,它允许相同的数字传递,当你按位时,按0并阻塞它,结果为0.现在,二进制的Long.MAX_Value是

0111111111111111111111111111111111111111111111111111111111111111 
Run Code Online (Sandbox Code Playgroud)

这是0后跟63 1s(总共是64位,它在java中很长)

因此,当你按下这个数字的位数和数字X然后你会得到相同的数字X,除了最左边的位现在变成零.这意味着您只更改了该数字的符号而不是值.

  • 发生碰撞的可能性有多大? (4认同)
  • 有什么办法可以从上述逻辑生成的long中取回UUID(或可能的UUID)? (3认同)

Dan*_*yev 9

正如其他人所写,长期没有足够的空间来存放唯一的数字.但在许多情况下,对于特定用途,数字可能是唯一的.例如,具有纳秒精度的时间戳通常足够好.为了得到它,将当前毫秒20位移位以分配空间纳秒,然后用纳秒覆盖它:

(System.currentTimeMillis() << 20) | (System.nanoTime() & ~9223372036854251520L);
Run Code Online (Sandbox Code Playgroud)

所述纳米&〜9223372036854251520L部分以当前的纳秒并且将第一44个字节为0,只留下右20个比特代表纳秒到一毫秒(999999毫微秒)是一样的:

nanoseconds & ~1111111111111111111111111111111111111111111100000000000000000000
Run Code Online (Sandbox Code Playgroud)

旁注:纳秒不应用于表示当前时间,因为它们的起点不是及时固定的,因为它们在达到最大值时会被回收.

您可以使用任何其他位操作.通常考虑当前时间和其他内容(例如当前线程ID,进程ID,ip)是很好的.

  • 发生碰撞的可能性有多大? (2认同)
  • 为什么不使用 **0xFFFFFL** 而不是 **~9223372036854251520L** ?`(System.currentTimeMillis() &lt;&lt; 20) | (System.nanoTime() &amp; 0xFFFFFL);` (2认同)

小智 8

看一下http://commons.apache.org/sandbox/commons-id//index.html 它有一个LongGenerator,它可以为您提供所需的产品.

此外,如果您正在使用Hibernate,那么您可以要求它为您生成ID(它有几种算法可供您选择),如果不是,您可以只看一下它们的实现,例如http://grepcode.com/ file/repo1.maven.org/maven2/hibernate/hibernate/2.1.8/net/sf/hibernate/id/TableHiLoGenerator.java#TableHiLoGenerator)


wcm*_*sen 6

这段代码的灵感来自 @Daniel Nuriyev 的回答。但是,当碰撞发生在同一毫秒内时,不使用纳米时间,而是使用计数器(或我所看到的鉴别器):

private static long previousTimeMillis = System.currentTimeMillis();
private static long counter = 0L;

public static synchronized long nextID() {
    long currentTimeMillis = System.currentTimeMillis();
    counter = (currentTimeMillis == previousTimeMillis) ? (counter + 1L) & 1048575L : 0L;
    previousTimeMillis = currentTimeMillis;
    long timeComponent = (currentTimeMillis & 8796093022207L) << 20;
    return timeComponent | counter;
}
Run Code Online (Sandbox Code Playgroud)

该方法通过将毫秒时间戳分量与计数器分量打包在一起来生成半唯一 ID。该算法允许在冲突开始发生之前的同一毫秒内生成大约一百万个(准确地说是 1048575 个)唯一 ID。直到 2248 年才会生成唯一 ID,届时它将回绕并再次从 0 开始。

ID 生成如下:

自纪元以来的毫秒数:

|0|000000000000000000000010110111101111100110001001111100101011111|

按位与 (8796093022207L):

|0|000000000000000000001111111111111111111111111111111111111111111|

为您提供 43 个最低有效位作为时间分量。

然后将其向左移动 20 位,得到:

|0|0010110111101111100110001001111100101011111|00000000000000000000|

与 20 位计数器(例如,如果计数器为 3)进行按位或,可得出:

|0|0010110111101111100110001001111100101011111|00000000000000000101|

时间分量仅使用 43 位(而不是 44 位),因为我们不希望更改最高有效位(即数字的符号)。这会导致仅生成肯定的 ID。