我有一个ImmutableSortedMap,用于保存以下数据结构中的数据。
<String, Pair<Long, Double>>
我的目标是从最大Double
开始按降序对此地图进行排序,如果两个Doubles
相同,请按Long
对其进行排序。如果Double
和Long
都相同,请按String
我目前的比较器仅处理第一种情况,看起来像这样。
private ImmutableSortedMap<String, Pair<Long, Double>> sortInDescendingSizeOrder(final Map<String, Pair<Long, Double>> statsByType) {
Ordering<String> ordering = Ordering
.from(new Comparator<Pair<Long, Double>>() {
@Override
public int compare(Pair<Long, Double> o, Pair<Long, Double> o2) {
return o.getRight().equals(o2.getRight()) ? o.getLeft().compareTo(o2.getLeft()) : o.getRight().compareTo(o2.getRight());
}
})
.reverse()
.onResultOf(Functions.forMap(statsByType, null));
return ImmutableSortedMap.copyOf(statsByType, ordering);
}
但是,它找到两个相同的Doubles
并抛出以下异常:Exception in thread "main" java.lang.IllegalArgumentException: Duplicate keys in mappings
我不明白我做错了什么......
编辑:我尝试按字符串(至少)添加compound
方法和顺序。
.compound(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
这使代码不会抛出任何异常,但是,结果映射中没有任何顺序。
编辑2:此时,任何顺序如上所述
Map<String, Pair<Long, Double>>
会做的。不一定需要是ImmutableSortedMap
答案 0 :(得分:3)
编辑:See Louis' answer 实际正确的解决方案(ImmutableMap.Builder#orderEntriesByValue(Comparator)
)。继续阅读此答案以修正错误的方法;)
这是ImmutableSortedMap#copyOf(Map, Comparator)
的记录行为:
抛出:
NullPointerException
- 如果地图中的任何键或值为nullIllegalArgumentException
- 如果任意两个键根据比较器相等
你得到的是根据比较器的值相等的结果,所以问题在那里。 See this answer for comparing map by value - 正如路易斯提到的那样(他来自番石榴队),使用Functions.forMap
很棘手。
在您的情况下,将.nullsLast()
添加到第一个订单并添加.compound(Ordering.natural())
应该有效。例如:
Map<String, Pair<Long, Double>> map = ImmutableMap.of(
"a", Pair.of(1L, 1.0d),
"b", Pair.of(1L, 1.0d),
"c", Pair.of(1L, 1.0d),
"d", Pair.of(1L, 1.0d)
);
@Test
public void should2()
{
final ImmutableSortedMap<String, Pair<Long, Double>> sortedMap = sortInDescendingSizeOrder(map);
assertThat(sortedMap).hasSize(4);
System.out.println(sortedMap); // {a=(1,1.0), b=(1,1.0), c=(1,1.0), d=(1,1.0)}
}
和
Map<String, Pair<Long, Double>> map = ImmutableMap.of(
"a", Pair.of(1L, 1.0d),
"b", Pair.of(2L, 1.0d),
"c", Pair.of(1L, 2.0d),
"d", Pair.of(2L, 2.0d)
);
输出{d=(2,2.0), b=(2,1.0), c=(1,2.0), a=(1,1.0)}
。
(sortInDescendingSizeOrder
供参考)
private ImmutableSortedMap<String, Pair<Long, Double>> sortInDescendingSizeOrder(
final Map<String, Pair<Long, Double>> statsByType) {
Ordering<String> ordering = Ordering.from(new Comparator<Pair<Long, Double>>() {
@Override
public int compare(Pair<Long, Double> o, Pair<Long, Double> o2) {
return ComparisonChain.start()
.compare(o.getRight(), o2.getRight())
.compare(o.getLeft(), o2.getLeft())
.result();
}
})
.reverse()
.nullsLast()
.onResultOf(Functions.forMap(statsByType, null))
.compound(Ordering.natural());
return ImmutableSortedMap.copyOf(statsByType, ordering);
}
ComparisonChain
是你可以在这里使用的番石榴的另一种礼物。
答案 1 :(得分:3)
ImmutableSortedMap
不正确的API。 ImmutableSortedMap
应该按键排序,这显然不是你正在做的事情。
更有意义的是写作
new ImmutableMap.Builder<String, Pair<Long, Double>>()
.orderEntriesByValue(comparing(Pair::getSecond).thenComparing(Pair::getFirst))
.putAll(map)
.build();
但是,如果你不能使用Java 8,你必须明确地写比较器
new Comparator<Pair<Long, Double>>() {
@Override public int compare(Pair<Long, Double> a, Pair<Long, Double> b) {
return ComparisonChain.start()
.compare(a.getSecond(), b.getSecond())
.compare(a.getFirst(), b.getFirst())
.result();
}
}