【问题标题】:Java UUID compareTo not working correctly for Type1 UUIDsJava UUID compareTo 对于 Type1 UUID 无法正常工作
【发布时间】:2020-05-19 22:08:14
【问题描述】:

在处理数据需要在 UUID 上排序的用例时,这些 UUID 都是 Type 1 或基于时间并使用 Datastax Cassandra Java 驱动程序库 (UUIDS.timebased()) 生成的,我发现 UUID.compareTo 没有排序一些 UUID 正确。 compareTo 中的逻辑是

    /**
 * Compares this UUID with the specified UUID.
 *
 * <p> The first of two UUIDs is greater than the second if the most
 * significant field in which the UUIDs differ is greater for the first
 * UUID.
 *
 * @param  val
 *         {@code UUID} to which this {@code UUID} is to be compared
 *
 * @return  -1, 0 or 1 as this {@code UUID} is less than, equal to, or
 *          greater than {@code val}
 *
 */
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))));
}

我使用 java 的 datastax cassandra 驱动程序生成了以下 2 个 UUID。

UUID uuid1 = java.util.UUID.fromString("7fff5ab0-43be-11ea-8fba-0f6f28968a17")
UUID uuid2 = java.util.UUID.fromString("80004510-43be-11ea-8fba-0f6f28968a17")
uuid1.timestamp() //137997224058510000
uuid2.timestamp() //137997224058570000

从上面可以看出 uuid1 小于 uuid2,但是当我们使用 UUID compareTo 方法比较它们时,我们得到不同的输出。我们应该得到 -1 的输出,因为它应该小于但我们得到的答案是 1,这表明这个 uuid1 大于 uuid2

uuid1.compareTo(uuid2) //output - 1

进一步分析,发现 uuid2 的 msb 转换为负数,而 uuid1 的 msb 为正数。因此, compareTo 中的逻辑返回值 1 而不是 -1。

u_7fff5ab0 = {UUID@2623} "7fff5ab0-43be-11ea-8fba-0f6f28968a17"
mostSigBits = 9223190274975338986
leastSigBits = -8090136810520933865

u_80004510 = {UUID@2622} "80004510-43be-11ea-8fba-0f6f28968a17"
mostSigBits = -9223296100696452630
leastSigBits = -8090136810520933865

这种行为对于 UUID 及其相互比较是否正常? 如果是这样,那么我们如何处理此类基于时间的 UUID 的排序?

谢谢

【问题讨论】:

  • 当一个大数变为负数时,这通常意味着发生了溢出。不知道这里会发生什么。

标签: java sorting cassandra datastax-java-driver timeuuid


【解决方案1】:

请注意,比较基于时间的 UUID 需要特别小心,From the docs

最后,请注意 Cassandra 的 timeuuid 排序与 UUID.compareTo(java.util.UUID) 不兼容,因此此方法创建的 UUID 不一定是后一种方法的下限。

不应将基于时间的 UUID 与 java.util.UUID#compareTo 进行比较。要比较两个基于时间的 UUID,您应该比较 时间;这两个 UUID 内包含。您需要自定义实用程序方法实现或仅比较两个时间戳。下面是一个例子:

// must be timebased UUID
int compareTo(UUID a, UUID b){
   return Long.compare(UUIDs.unixTimestamp(a),UUIDs.unixTimestamp(b));
}

要了解更多信息,请浏览此DOCS

【讨论】:

  • 感谢您的回复@rahul,您上面提到的文档仅针对 UUIDs.startOf 和 UUIDs.endOf 方法添加,它们为给定的时间戳提供最低和最高可能的 UUID。所以评论是有道理的,因为它们是假的 UUID。但是应该为 UUIDs.timebased() 方法提供类似的注释,该方法实际上生成了无法使用 java.util.UUID.compareTo 进行排序的 UUID。这让我很困惑。
  • 嗯,实际上你需要了解类型 1 UUID 的创建方式。 UUID 位的前半部分代表时间不一定是真的。这就是为什么 cassandra 有不同的实现来比较 UUID。请浏览此 wiki 页面以了解 UUID 是如何制作的。 en.wikipedia.org/wiki/Universally_unique_identifier。希望你能得到关于为什么 UUID-v1 的 compareTo 方法失败的答案。仔细检查这些 128 位是如何填充的。
  • uuidtools.com/uuid-versions-explained -> 你也可以查看这些。 @blankCoder
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-13
  • 1970-01-01
  • 2013-07-31
  • 1970-01-01
  • 1970-01-01
  • 2021-02-09
  • 2021-04-06
相关资源
最近更新 更多