我想可以使用 UUID.randomUUID().getMostSignificantBits(),但有时会生成一些负数的长整型,这对我来说是个问题。
是否可能从UUID中仅生成正数的长整型?由于将有数十亿条目,因此我希望每个生成的密钥都必须是唯一的。
UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE
这能起作用的原因是,当你使用&运算符与1进行操作时,它允许相同的数字通过,而当你使用&运算符与0进行操作时,它将其阻止并且结果为0。现在,Long.MAX_Value在二进制中表示为:0111111111111111111111111111111111111111111111111111111111111111
这是一个0后面跟着63个1的二进制数(总共64位,在Java中被称为长整型)
当您将一个数字X与上述数字进行按位&运算时,您将得到相同的数字X,只不过最左边的位现在变成了零。这意味着您只更改了该数字的符号而没有更改其值。
就像其他人所写的一样,long类型的空间不足以存储唯一的数字。但在许多情况下,一个数字可能已经足够特定用途的唯一性要求。 例如,具有纳秒精度的时间戳通常已经足够好了。 要获得它,请将当前毫秒数左移20位以为纳秒分配空间,然后将其覆盖在纳秒上:
(System.currentTimeMillis() << 20) | (System.nanoTime() & ~9223372036854251520L);
nano & ~9223372036854251520L部分获取当前纳秒数并将前44个字节设置为0,只保留右侧20位,表示多达一毫秒的纳秒数(999999纳秒)。
它与以下内容相同:
nanoseconds & ~1111111111111111111111111111111111111111111100000000000000000000
旁注:毫微秒不应用于表示当前时间,因为它们的起始点在时间上不固定,并且当它们达到最大值时会被重置。
你可以使用任何其他位操作。通常最好考虑当前时间和其他一些信息,例如当前线程ID、进程ID、IP地址等。
9223372036853727232L
相与吗?数字9223372036854251520L
只会取nano-time的最右19位。 - wcmatthysen(System.currentTimeMillis() << 20) | (System.nanoTime() & 0xFFFFFL);
- codeisee请查看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)
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;
}
nextID
方法由于标记了synchronized
关键字而是线程安全的(因此不需要额外的工作来使其线程安全)。将方法本身同步比使用AtomicLong
更简单。如果您对previousTimeMillis
和counter
变量都使用AtomicLong
,则仍然需要一个锁定对象来确保这两个变量被原子更新。 - wcmatthysen我刚刚发现了这个解决方案。目前我正在努力理解这个解决方案。它是基于Twitter雪花ID生成算法的Java实现,用于生成64位顺序ID。
https://github.com/Predictor/javasnowflake
欢迎提出任何建议。
您还可以使用时间排序标识符(TSID),它是从Twitter的Snowflake ID派生而来。
TSID与其他在此处展示的想法非常相似。没有必要解释别人很久以前就解释得很好的内容。
示例代码:
public class TSID {
private static final int RANDOM_BITS = 22;
private static final int RANDOM_MASK = 0x003fffff;
private static final long TSID_EPOCH = Instant.parse("2020-01-01T00:00:00.000Z").toEpochMilli();
private static final AtomicInteger counter = new AtomicInteger((new SecureRandom()).nextInt());
public static long next() {
final long time = (System.currentTimeMillis() - TSID_EPOCH) << RANDOM_BITS;
final long tail = counter.incrementAndGet() & RANDOM_MASK;
return (time | tail);
}
}
以上代码是从Tsid复制并改编而来的一个小例子。它可以每毫秒生成多达4194304个ID,假设只有一个ID生成器且不会发生冲突。
如果您担心安全问题,例如生成时间泄漏和序列猜测,请查看Francesco的ID Encryptor库。
请注意,如果您打算在Javascript中使用64位标识符,该语言只支持52位精度,您需要将其转换为某些字符串格式,例如十六进制、基32、基62等。
有关更多信息,请阅读Vlad撰写的这些优秀文章:
String insertSQL = "INSERT INTO table... (name, ...)"
+ " VALUES(?, ..., ?)";
try (Connection connection = getConnection();
PreparedStatement stmt = connection.prepareStatement(insertSQL,
Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, ...);
stmt.setInt(2, ...);
stmt.setBigDecimal(3, ...);
...
stmt.executeUpdate();
ResultSet keysRS = stmt.getGeneratedKeys();
if (keysRS.next()) {
long id = keysRS.getInt(1);
}
}
^
)。