Java异常:比较方法违反了其通用约定

3

我已经查看了有关此主题的先前帖子-- 这篇文章这篇文章。尽管如此,我仍然无法弄清楚下面代码中违反合同的原因。

public class ScoreComparator 
implements Comparator<Map.Entry<?, Double>> {

public int compare(Map.Entry<?, Double> o1, 
        Map.Entry<?, Double> o2) {
    return o1.getValue().compareTo(o2.getValue());
}   
}

我使用它的方法如下:
List<Entry<String, Double>> entryList = 
        new ArrayList<Entry<String, Double>>(
                iterTypeScoreMap.get(keyToSort).entrySet());
Collections.sort(entryList, new ScoreComparator());

并且iterTypeScoreMap的声明如下:
ConcurrentHashMap<String, Map<String, Double>> iterTypeScoreMap;

这个映射表(iterTypeScoreMap)在排序时可能会发生变化,所以我复制了列表,并对其进行排序。

由于我使用的是Double的内置compareTo方法,那么这不应该解决问题吗?另一个使调试变得困难的因素是,这个异常并不总是发生;只有某些运行时才会发生。这里可能存在什么错误呢?

提前致谢。


1
你是直接使用这个比较器来对 SortedMap 进行排序吗?还是你将其用于单独的集合(例如 List<Map.Entry<?, Double>>)中? - Jack
3
在排序过程中,你的地图中的值是否有可能发生改变? - Sbodd
@Jack,好观点。已经在编辑问题时包含了它。 - Raghava
@Sbodd,请检查编辑。它已经改变了。 - Raghava
2个回答

3
你遇到的问题是在排序操作期间,由于条目值(分数)同时被更改而导致排序顺序发生变化。
当条目本身正在被并发修改时,复制地图的entrySet()是没有用的。你需要深度复制整个集合,也就是复制所有的Entry对象,才能防止错误发生。目前,你正在排序的列表具有与原始映射相同的Entry对象引用。

是的,我认为那就是问题所在。谢谢您。我根据映射条目创建了AbstractMap.SimpleImmutableEntry,并将它们插入到要排序的列表中。这似乎已经解决了问题。 - Raghava

0

我认为问题在于 a.compareTo(b)==0 应该意味着 a.equals(b)

然而,如果你的键不同但值相同,那么这两个值将会有 a.compareTo(b)==0 但是 a.equals(b) 将会是 false


不,这并不是暗示。只是强烈建议。 - Jack
我认为问题提出者所使用的任何排序算法都可能会假定(尽管是错误的)这一点。我知道TreeSet也是这样做的,它会删除不相等但a.compare(b)==0的元素。 - k_g
TreeMap 文档中:即使排序与等于不一致,有序映射的行为仍然是明确定义的;它只是未遵守 Map 接口的一般契约。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接