如果每个项目有 2 个 ID,如何为 RecyclerView 生成唯一 ID?

How to generate unique ids for RecyclerView, in case of 2 ids per item?

背景

假设我有一个 RecyclerView,其中的项目只有在您查看它们具有的 2 个 ID 时才能是唯一的,而不仅仅是其中一个。

第一个 Id 是主要的。通常不会有 2 个项目具有相同的主 ID,但有时可能会出现,这就是为什么要有次 ID。

在我的

问题

RecyclerView 适配器需要返回 "long" 类型:

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemId(int)

我试过的

克服这个问题的简单方法是拥有一个 HashMap 和一个计数器。

HashMap 将包含组合键,值将是应返回的 ID。计数器用于在新的组合键的情况下生成下一个 id。在这种情况下,组合键可以是 "Pair" class。

假设 RecyclerView 数据中的每个项目都有 2 个长型键:

HashMap<Pair<Long,Long>,Long> keyToIdMap=new HashMap();
long idGenerator=0;

这是在 getItemId 中要做的事情:

Pair<Long,Long> combinedKey=new Pair(item.getPrimaryId(), item.getSecondary());
Long uniqueId=keyToIdMap.get(combinedKey);
if(uniqueId==null) 
  keyToIdMap.put(combinedKey,uniqueId=idGenerator++);
return uniqueId;

这有占用越来越多内存的缺点。虽然不多,而且它非常小并且与您已有的数据成正比,但仍然...

然而,这具有能够处理所有类型的 ID 的优势,并且您可以根据需要使用更多的 ID(只需要类似于 Pair 的东西)。

另一个优点是它会使用所有从0开始的ID。

问题

是否有更好的方法来实现这一点?

也许是一种数学方法?我记得我过去曾学习过使用质数来完成类似的任务。它会在这里工作吗?

现有的主要和次要 ID 是否使用整个 64 位范围的长整型?如果不是,则可以使用例如位分片从它们的值计算出唯一的 64 位长。

另一种方法是将两者与具有非常低冲突的散列(例如像 SHA2 的加密散列)一起散列,并使用结果的前 64 位。拥有 64 位的范围意味着您可以轻松地拥有数百万个项目,然后才有可能发生碰撞 - 当您添加 sqrt(64)=2**32 项目时,碰撞的可能性为 50%,这超过 4十亿

最后,拥有一个独特的独立映射是非常通用的,并且假设映射总是可以访问它很好(当您尝试跨机器等同步新 ID 时会变得很棘手)。在 Java 中,您可以尝试通过避免盒装 Longs 和使用自定义映射实现的单独 Pair 实例来提高性能,但这是微优化。

使用 SHA1 的示例:

With Guava - 用法简洁明了。

HashFunction hf = Hashing.sha1();
long hashedId = hf.newHasher()
       .putLong(primary)
       .putLong(secondary)
       .hash()
       .asLong();

只是标准 JDK,它非常可怕,但可能会更有效,应该看起来像这样(我忽略了已检查的异常):

static void updateDigestWithLong(MessageDigest md, long l) {
  md.update((byte)l);
  md.update((byte)(l >> 8));
  md.update((byte)(l >> 16));
  md.update((byte)(l >> 24));
}

// this is from the Guava sources, can reimplement if you prefer
static long padToLong(bytes[] bytes) {
  long retVal = (bytes[0] & 0xFF);
  for (int i = 1; i < Math.min(bytes.length, 8); i++) {
    retVal |= (bytes[i] & 0xFFL) << (i * 8);
  }
  return retVal;
}

static long hashLongsToLong(long primary, long secondary) {
  MessageDigest md = MessageDigest.getInstance("SHA-1");
  updateDigestWithLong(md, primary);
  updateDigestWithLong(md, secondary);
  return padToLong(md.digest());
}

我认为我最初的想法是我能想到的最好的。 应该覆盖所有可能的 ID,尽可能少的冲突。