我可以使用identityHashCode在具有相同性的对象之间生成compareTo吗



我想在两个对象之间实现一个简单的比较器,它们的唯一要求是

  1. 它是一个有效的比较器(即定义所有对象的线性顺序),并且
  2. 当且仅当对象相同时,.compare将返回0

Comparator.comparing(System::identityHashCode)能工作吗?还有别的办法吗?

动机:我想建立一个集合,允许我将带时间戳的消息存储在线程安全的集合中,该集合将支持诸如"给我所有时间戳位于[a,b)".中的消息

Guava的TreeMultimap似乎使用了全局锁(编辑:如果使用synchronizedSortedSetMultimap包装器包装),而ConcurrentSkipListMap似乎每次只支持一个条目(它是一个映射,而不是多映射)。所以我想只使用一组配对:

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>>(具有线程安全的Set<>实例),但在内存方面似乎有点浪费,一旦删除消息,我需要自己删除emptySet以节省内存。

编辑:正如一些人所指出的,identityHashCode可能会产生冲突,事实上,我现在已经确认在我的设置中存在这样的冲突(这大致相当于有如上所述的4K集合,每个集合每个时间仓填充4K消息)。这很可能是我看到一些消息被丢弃的原因。因此,我现在比以往任何时候都更感兴趣的是找到一种";不可知论者;比较运算符,真正尊重相同性。实际上,64位散列值(而不是identityHashCode提供的32位值)可能就足够了。

虽然不能保证,但我怀疑这导致问题的可能性微乎其微。

System.identityHashCode返回Object.hashCode在未被覆盖时将返回的值,包括在文档中:

Object类定义的hashCode方法确实为不同的对象返回了不同的整数,这是合理可行的。

因此;尽可能实用";足够的虽然不能保证,但如果你遇到这种情况会导致问题,我会感到非常惊讶。您必须有两个时间戳完全相同的消息,其中JVM的Object.hashCode实现为这两个消息返回相同的值。

如果这种巧合的结果是";"核电站爆炸";那么我就不会冒险了;我们没有向客户收取账单"-或者甚至";我们向客户开了两次账单,可能会被起诉;如果没有更好的选择,我可能会接受这个机会。

@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(它使用一个内存化的AtomicInteger计数器来分配uid)。

在"中编写(也许不太容易阅读?)所需的带时间戳的消息容器也很容易;一个";行:

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

Comparator.comparising(System::identityHashCode)能工作吗?还有别的办法吗?

如前所述,identityHashCode不是唯一的。

实际上,64位哈希值(而不是identityHashCode提供的32位值)可能就足够了

我认为这只是减少重叠的机会,而不是消除它们。哈希算法被设计为限制重叠,但通常不能保证没有重叠。例如,MD5是128位,并且仍然有重叠。

不如用AtomicLong为每条消息分配一个唯一的号码。然后你的比较功能会做:

  1. 按时间进行比较。如果可能的话,我会用long代替float
  2. 如果同时,则按唯一值进行比较

如果有多个系统接收这些消息,则需要记录唯一的系统id和消息编号以确保唯一性。

相关内容

  • 没有找到相关文章

最新更新