如果每个项目有 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" 类型:
我试过的
克服这个问题的简单方法是拥有一个 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,尽可能少的冲突。
背景
假设我有一个 RecyclerView,其中的项目只有在您查看它们具有的 2 个 ID 时才能是唯一的,而不仅仅是其中一个。
第一个 Id 是主要的。通常不会有 2 个项目具有相同的主 ID,但有时可能会出现,这就是为什么要有次 ID。
在我的
问题
RecyclerView 适配器需要返回 "long" 类型:
我试过的
克服这个问题的简单方法是拥有一个 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,尽可能少的冲突。