【问题标题】:ConcurrentSkipListMap sorting: Can it be done by the value's compareTo?ConcurrentSkipListMap 排序:可以通过值的 compareTo 来完成吗?
【发布时间】:2010-09-21 16:41:05
【问题描述】:

在游戏中,我试图保留一个用户列表并按分数对其进行排序,以便我可以在任何给定时间查询该列表并返回(例如)按分数排名前十的用户。这个列表应该是线程安全的。我设想使用 userName 字符串作为键,值将是实现 Comparable 并具有 displayName 和 score 等属性的 User 对象。因此,User 对象将具有一个 compareTo 方法,该方法将比较 score 属性以确定其位置。

我正在考虑为此使用 ConcurrentSkipListMap,但据我所知,Map(而不是 Set)使用键进行排序。我想让列表按 User 对象的 score 属性排序,但仍然使用 Map 因为我需要能够访问任何给定用户并从线程修改他们的 score 属性。

使用我自己的 Comparator 作为键似乎不能解决我的问题,因为我怀疑我是否可以访问关联的值进行比较。我可以使用 ConcurrentSkipListSet 但访问列表以修改单个用户的分数将是(我想)一项昂贵的操作(由于每次都需要迭代)。

有人能建议如何做到这一点吗?

【问题讨论】:

    标签: java sortedset sortedmap


    【解决方案1】:

    不,我认为你不能。用于排序的比较器与用于索引的比较器相同。您可能必须维护 2 个集合。一种用于保持用户分数的顺序,用于按名称引用用户。

    【讨论】:

    • 谢谢你的回复,我很害怕。 map.values().toArray() 然后在数组对象上调用 sort 会是一个昂贵的操作吗?由于 User 对象实现了 Comparable 它会工作(我测试过)。但是,如果我在 Collection 中有 50,000 个对象,这是否明智?
    • 这取决于你的性能限制在哪里。如果高分只是偶尔访问,那么它可能没问题。您可以实现一个包含前 n 个值的自定义集合(类似堆的结构)。
    • 快速更新:我使用 50,000 个对象进行了测试。在 map.values() 上调用 toArray 需要 105 毫秒。对 Array(使用 User 对象的 compareTo 方法)调用 sort() 需要 103 毫秒。我想性能会做到,但我不禁认为有一个更优雅的解决方案,它不会花费近四分之一秒的时间来运行。再次感谢您的回复。
    • 马特,首先,我不相信 values() 这么慢,可能你有一个有缺陷的微基准。其次,您可以只迭代地图的条目并维护一个包含十个元素的 TreeMap(添加前十个元素后,您删除每个额外添加的最少元素)。
    【解决方案2】:

    get(key) 取决于比较器(以便能够定位密钥)。您提出了一个依赖于get(key) 的比较器(以访问键的映射值并基于此进行比较)。这必然会导致无限递归和堆栈溢出(从好的方面来说,您在正确的网站上发帖!!)

    【讨论】:

    • 嘿,触摸'。我当然明白为什么它不起作用,我只是希望我的例子能帮助说明我正在尝试做的事情。 :)
    【解决方案3】:

    迈克尔是对的,你不能一边吃蛋糕一边吃;)

    我认为你有 3 个选择:

    1. 使用地图可以快速更新用户的分数,并且在排序以找到最高分数时需要付出代价。
    2. 使用按分数排序的 SortedSet,以便快速找到最高分数,但更新用户分数时必须付出代价
    3. 维护两个数据结构,这样你就可以得到最好的 1 和 2。例如,你有一个按分数排序的集合中的真实数据,但还维护一个用户名映射到集合或类似的索引.这样你总是有排序的分数,更新用户的分数只是查找,而不是搜索。您为此付出的代价是现在您在两个地方维护一些重复的信息,尤其是考虑到并发访问,确保两个地方始终同步更新可能会很棘手。

    我不会对 1 和 2 之间哪个更快做出假设。我会根据您的预期使用情况对它们进行尝试,并衡量哪个最差。

    如果您真的只对前 n 个分数感兴趣,那么可以单独维护该列表。因此,让您的用户名地图为每个人评分,但也要保持一小部分最高分(及其用户)。每次添加/更新某人的分数时,只需对照最高分数列表检查分数,如果它大于那里的最小分数,只需添加它并剔除较低的分数。这与上面的建议 3 类似,但开销较小,而且可能更易于维护。

    【讨论】:

    • 谢谢,很好的回应。我没有涉及一些更复杂的方法,我将排序/比较分数和用户,所以我认为我的转换为数组然后排序的方法现在必须这样做。我想我会在主线程中执行强制转换操作,然后生成工作线程(是的,我正在使用线程池)来执行我的各种操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 2023-01-31
    相关资源
    最近更新 更多