ImmutableSortedMap - 映射中的重复键

时间:2016-11-22 10:23:28

标签: java sorting guava comparator

我有一个ImmutableSortedMap,用于保存以下数据结构中的数据。

<String, Pair<Long, Double>>

我的目标是从最大Double开始按降序对此地图进行排序,如果两个Doubles相同,请按Long对其进行排序。如果DoubleLong都相同,请按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

2 个答案:

答案 0 :(得分:3)

编辑:See Louis' answer 实际正确的解决方案(ImmutableMap.Builder#orderEntriesByValue(Comparator))。继续阅读此答案以修正错误的方法;)

这是ImmutableSortedMap#copyOf(Map, Comparator)的记录行为:

  

抛出:

     

NullPointerException - 如果地图中的任何键或值为null   IllegalArgumentException - 如果任意两个键根据比较器相等

你得到的是根据比较器的值相等的结果,所以问题在那里。 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();
  }
}