微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我可以使用 identityHashCode 在尊重相同性的对象之间生成 compareTo 吗?

如何解决我可以使用 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 在未被覆盖时将返回的值,包括此 in the documentation

在合理可行的范围内,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)。

在“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)))
,

Comparator.comparing(System::identityHashCode) 会起作用吗?还有别的方法吗?

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

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

我认为这只会减少重叠的机会,而不是消除它们。哈希算法旨在限制重叠,但通常不能保证没有重叠。比如MD5是128位的,还是有重叠的。

AtomicLong 为每条消息分配一个唯一编号如何。然后你的比较函数会做:

  1. 按时间比较。如果可能,我会使用 long 而不是 float。
  2. 如果相同,则按唯一值进行比较。

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?