【问题标题】:ImmutableSortedMap - Duplicate keys in mappingsImmutableSortedMap - 映射中的重复键
【发布时间】:2016-11-22 10:23:27
【问题描述】:

我有一个 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

【问题讨论】:

    标签: java sorting guava comparator


    【解决方案1】:

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


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

    投掷:

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

    你得到的是根据比较器的值相等的结果,所以问题就在那里。 See this answer for comparing map by value - 正如 Louis 提到的(他来自 Guava 团队),使用 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 是 Guava 的另一个好东西,您可以在这里使用。

    【讨论】:

    • 排序还是一团糟...绝对没有排序,不是在双,长或字符串上
    • 我编辑了答案并添加了包含{a=(1,1.0),b=(2,1.0),c=(1,2.0),d=(2,2.0)} 的输入映射,输出对我来说看起来不错:{d=(2,2.0), b=(2,1.0), c=(1,2.0), a=(1,1.0)}。您能否发布您输入的示例数据以及您的预期输出?
    • 它有效,我在其他地方出错了。谢谢@Xaerxess
    • 生成的地图仍然会抛出get(keyNotInMap)。在这里使用ImmutableSortedMap 根本不是一个好主意。
    • @LouisWasserman 是的,.nullsLast() 不见了。正如所引用的那样,这很棘手 - 添加了对您更简单答案的参考。
    【解决方案2】:

    ImmutableSortedMap 不是正确的 API。 ImmutableSortedMap 应该按 keys 排序,而这显然不是你在做的。

    写作更有意义

    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();
      }
    }
    

    【讨论】:

    • 关于ImmutableMap.Builder#orderEntriesByValue - 很整洁(没见过这个,是@since 19.0)! OP 想要对两个对值进行排序,并且似乎在 Java ComparisonChain 似乎就在这里。
    猜你喜欢
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 2010-11-06
    相关资源
    最近更新 更多