假设我有一个RecyclerView,其中包含的项目只有在你看到2个ID时才能是唯一的,而不仅仅是其中一个。
第一个ID是主要ID。通常,没有2个项目具有相同的主ID,但有时可能会发生,这就是为什么存在辅助ID。
在我的
RecyclerView适配器需要有一个" long"返回类型:
解决这个问题的简单方法是使用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。
是否有更好的方法来实现这一目标?
也许是一种数学方式?我记得我过去曾学过使用素数来完成类似的任务。它会在某种程度上起作用吗?
答案 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,尽可能少碰撞。