我能用identityhashcode在对象之间产生一个比较吗?

ulmd4ohb  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(403)

我想在两个对象之间实现一个简单的比较器,其唯一要求是
它是一个有效的比较器(即定义所有对象的线性顺序),并且 .compare 当且仅当对象相同时返回0。
威尔 Comparator.comparing(System::identityHashCode) 工作?还有别的办法吗?
动机:我想构建一个集合,允许我将时间戳消息存储在线程安全的集合中,该集合将支持“获取时间戳位于[a,b]中的所有消息”之类的查询。
看来Guava TreeMultimap 使用全局锁(编辑:如果用 synchronizedSortedSetMultimap Package ),以及 ConcurrentSkipListMap 似乎每次只支持一个条目(它是一个Map,而不是多Map)。所以我想只用一对: ConcurrentSkipListSet<ImmutablePair<Float,Message>> db ,
其中成对词按词汇顺序排列,首先按时间(使用 Float.compareTo )然后通过类似的方式 Comparator.nullsFirst(Comparator.comparing(System::identityHashCode)) .
这个 nullsFirst 就这么简单吗 db.subSet(ImmutablePair.of(a,null), ImmutablePair.of(b,null)) 查询半开放时间间隔[a,b]。
你知道为什么我关心比较器保持相同:如果消息比较器为不相同的消息返回零,消息可能会被删除。
您还可以看到为什么我不需要比较器的其他功能:它就在那里,这样我就可以使用 ConcurrentSkipListSet . 我当然不想强加给用户(好吧,只有我:-)来实现 Message .
另一个可能的解决方案是使用 ConcurrentSkipListMap<Float, Set<Message>> (使用thread safe set<>示例)但是在内存方面似乎有点浪费,一旦消息被删除,我就需要自己删除emptyset以节省内存。
编辑:正如一些人所指出的,identityhashcode可能会产生冲突,事实上,我现在已经确认在我的设置中存在这样的冲突(这大致相当于上面的4k集合,每个时间段都填充4k消息)。这很可能是我看到一些邮件被丢弃的原因。因此,我现在比以往任何时候都更感兴趣的是找到一个“不可知论”的比较运算符,它真正尊重相同性。实际上,64位散列值(而不是identityhashcode提供的32位值)可能就足够了。

pinkon5k

pinkon5k1#

正如@stuartmarks在评论中指出的那样,guava支持 Ordering.arbitrary() ,它提供线程安全的冲突处理。该实现有效地利用了 identityHashCode :

@Override
public int compare(Object left, Object right) {
  if (left == right) {
    return 0;
  } else if (left == null) {
    return -1;
  } else if (right == null) {
    return 1;
  }
  int leftCode = identityHashCode(left);
  int rightCode = identityHashCode(right);
  if (leftCode != rightCode) {
    return leftCode < rightCode ? -1 : 1;
  }

  // identityHashCode collision (rare, but not as rare as you'd think)
  int result = getUid(left).compareTo(getUid(right));
  if (result == 0) {
    throw new AssertionError(); // extremely, extremely unlikely.
  }
  return result;
}

所以只有在哈希冲突的情况下, getUid 调用(它使用一个已记忆的原子整数计数器来分配uid)。
在“one”行中编写所需的带时间戳的消息容器也非常容易(可能不太容易阅读?):

db = new ConcurrentSkipListSet<>(
                (Ordering.<Float>natural().<ImmutablePair<Float,Message>>onResultOf(x -> x.left))
                        .compound(Ordering.arbitrary().nullsFirst().<ImmutablePair<Float,Message>>onResultOf(x -> x.right)))
eeq64g8w

eeq64g8w2#

虽然这不能保证,但我怀疑这导致问题的可能性非常小。 System.identityHashCode 返回 Object.hashCode 如果不重写,将返回,包括在文档中:
尽可能地实际,由类对象定义的hashcode方法为不同的对象返回不同的整数。
那么,“尽可能实际”就足够了吗?虽然这不能保证,但如果你遇到这样的情况,我会非常惊讶。您必须有两个时间戳完全相同的消息,并且jvm的位置完全相同 Object.hashCode 实现为两条消息返回相同的值。
如果巧合的结果是“核电站爆炸”,那么我就不会冒险了。如果这种巧合的结果是“我们没有给客户开账单”——甚至是“我们给客户开了两次账单,可能会被起诉”,如果没有更好的替代建议,我可能会接受这个机会。

t9aqgxwy

t9aqgxwy3#

comparator.comparing(system::identityhashcode)能工作吗?还有别的办法吗?
如前所述,identityhashcode不是唯一的。
实际上,64位散列值(而不是identityhashcode提供的32位值)可能就足够了
我认为这只是减少重叠的机会,而不是消除重叠。散列算法被设计用来限制重叠,但通常不能保证没有重叠。例如,md5是128位的,仍然有重叠。
给每封邮件分配一个唯一的号码怎么样 AtomicLong . 那么你的比较函数会:
按时间比较。如果可能的话,我会用long代替float。
如果相同,则按唯一值进行比较。
如果有多个系统接收这些消息,则需要记录唯一的系统id和消息编号,以确保唯一性。

相关问题