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

时间:2017-11-15 08:10:45

标签: android android-recyclerview uniqueidentifier

背景

假设我有一个RecyclerView,其中包含的项目只有在你看到2个ID时才能是唯一的,而不仅仅是其中一个。

第一个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"在这种情况下的类。

假设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。

问题

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

也许是一种数学方式?我记得我过去曾学过使用素数来完成类似的任务。它会在某种程度上起作用吗?

2 个答案:

答案 0 :(得分:0)

现有的主要和辅助ID是否使用整个64位长的?如果没有,则可以通过例如位切片从其值计算唯一的64位长。

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

最后,拥有一个独特的独立映射是非常通用的,假设地图总是可访问它很好(当你尝试在机器上同步新的id时会变得棘手)。在Java中,您可以尝试通过避免使用自定义地图实现的盒装Long和单独的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());
}

答案 1 :(得分:0)

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