【问题标题】:Having a Multimap sorted on keys only in Java仅在 Java 中对键进行排序的 Multimap
【发布时间】:2011-07-26 23:11:35
【问题描述】:

我想要一个仅基于键排序的c.g.c.c.Multimap。这些值不应该被排序。我尝试用番石榴的TreeMultimap 构建一些东西,但是我不能使用它,因为值类型没有实现Comparable

public class MyObject /* doesn't implement Comparable */ {
  private String name;
  private int score;
  // Getters/setters are implemented
  public static Function<MyObject,Integer> myObjectToScore {
    @Override public Integer apply (MyObject o) { return o.score; }
  }
  public static Multimap<Integer,MyObject> indexOnScore(Iterable<MyObject> i) {
    Multimap<Integer,MyObject> m = Multimaps.index(i, myObjectToScore());
    // Do the sort of the keys.
    return m;
  }
}

我曾考虑过获取键的SortedSet,然后遍历排序集中的每个键以获取各种值,但我希望使用 Guava 中现有的(尚未发现的)功能,而不是使用这种hack

注意:我不会让MyObject 实现Comparable,因为它对我的实际对象没有意义。


输入/输出示例:

Set<MyObject> s = Sets.newHashSet(
  new MyObject("a", 2),
  new MyObject("b", 3),
  new MyObject("c", 1),
  new MyObject("d", 3),
  new MyObject("e", 1)
); // Assuming constructor MyObject(String name, int score)

for (Map.Entry<Integer, MyObject> e: MyObject.indexedOnScore(s).entries()) {
  System.out.printf("%d -> %s%n", e.getKey(), e.getValue().getName());
}

打印:

1 -> c // or switched with line below
1 -> e
2 -> a
3 -> b // or switched with line below
3 -> d

【问题讨论】:

  • 你能举个例子输入/输出
  • @Dan:是的,我确实知道那门课,但我在这里说的是 Guava 的 Multimaps,而不是 Map
  • 什么是c.g.c.c?请不要在问题中使用这样的缩写。

标签: java sorting guava multimap


【解决方案1】:

Multimaps.index 返回一个ImmutableListMultimap,因此您将无法在创建后对其进行排序。但是,您可以先创建一个已排序的 Iterable&lt;MyObject&gt; 副本,然后将其提供给 Multimap.index...ImmutableListMultimap 保持事物的顺序与提供给它们的顺序相同。

public static ImmutableMultimap<Integer, MyObject> indexOnScore(Iterable<MyObject> i) {
  List<MyObject> sorted = Ordering.natural().onResultOf(myObjectToScore())
      .sortedCopy(i);
  return Multimaps.index(sorted, myObjectToScore());
}

另一种选择可能是创建一个TreeMultimap 并使用Ordering.arbitrary() 作为值的Comparator

【讨论】:

  • 是的,这就是我要找的。你给了我两种可能性,而不是一种。非常感谢!编辑:然而,我不喜欢这两种连续,但我会尽量应付它。
  • @ogregoire:嗯,它实际上并没有排序两次......它只是排序一次,然后以相同的顺序创建Multimap。它确实将数据从Iterable 复制到中间List
  • 你也可以使用 ImmutableMultimap.builder().orderKeysBy();或者您可以使用 Multimaps.newMultimap() 来选择支持地图和集合(使用 TreeMap 和 ArrayList)。
  • 很好...之前没有注意到ImmutableMultimap.builder().orderKeysBy()
  • @ColinD 我认为将 Kevin 的评论包含在您的答案中会非常有用。
【解决方案2】:

MultimapBuilder 在 Guava 16 中引入:

<K extends Comparable<? super K>, V> ListMultimap<K, V> multimap() {
    return MultimapBuilder.treeKeys().linkedListValues().build();
}

这使您的键按其自然顺序排序(MultimapBuilder::treeKeys 也被重载以接受自定义比较器),并且与每个键关联的值保存在 LinkedList 中(ArrayListHashSet 属于其他选项)。

【讨论】:

    【解决方案3】:

    虽然 OP 的具体情况似乎已经使用不可变的多映射构建函数得到了回答,但我需要一个他所要求的可变版本。如果它可以帮助任何人,这是我最终创建的通用方法:

    static <K, V> Multimap<K, V> newTreeArrayListMultimap(
        final int expectedValuesPerKey)
    {
        return Multimaps.newMultimap(new TreeMap<K, Collection<V>>(),
            new Supplier<Collection<V>>()
            {
                @Override
                public Collection<V> get()
                {
                    return new ArrayList<V>(expectedValuesPerKey);
                }
            });
    }
    

    【讨论】:

      【解决方案4】:

      调用Multimaps.newMultimap,它使您可以灵活地创建例如由TreeMap 支持的Multimap,其值为ArrayLists。

      【讨论】:

      • 有趣的是,您编写了 Multimaps 代码,但似乎没有人注意到
      【解决方案5】:

      我想指出,替代建议的解决方案,即“创建 TreeMultimap 并使用 Ordering.arbitrary() 作为值的比较器”,只有在 MyObject 没有'的情况下才有效t 覆盖 equals() 或 hashcode()。 Ordering.arbitrary() 与 equals 不一致,而是使用对象标识,这使得与 TreeSet 结合使用不是一个好主意。

      【讨论】:

        【解决方案6】:

        这个怎么样:

            public static Multimap<Integer, MyObject> indexOnScore(Iterable<MyObject> i) {
                Multimap<Integer, MyObject> m = Multimaps.index(i, myObjectToScore());
        
                Multimap<Integer, MyObject> sortedKeys = Multimaps.newMultimap(
                        Maps.<Integer, Collection<MyObject>>newTreeMap(),
                        new Supplier<Collection<MyObject>>() {
                            @Override
                            public Collection<MyObject> get() {
                                return Lists.newArrayList(); // Or a Set if appropriate
                            }
                        }
                );
        
                sortedKeys.putAll(m);
        
                return sortedKeys;
            }
        

        不过,在这种情况下,创建两个单独的 Multimaps 会产生开销。

        【讨论】:

        • 如你所说,创建两个不同的Multimaps 太多了。
        • 是的,我自己也更喜欢@ColinD 的回答。
        【解决方案7】:

        如果您使用比较器,您可以使用 TreeMultimap 来完成。

        为键类型和值类型(MyObject?)创建一个Comparator。然后使用create(Comparator keyComparator, Comparator valueComparator)制作地图。

        与实现 Comparable 相比,使用 Comparator 的好处是您可以使 Comparator 特定于您想要使用地图的情况,并且一般不会影响您的对象。只要您的 Comparator 与 equals 一致,它就可以为所欲为。

        【讨论】:

        • 我不太喜欢这个。我说让MyObject 实现Comparable 毫无意义。此声明的扩展是我在逻辑上也没有MyObject 的“默认”Comparator。我的目标实际上是拥有一个对其键进行排序的 Multimap,但不应对一个键的值进行排序。
        【解决方案8】:

        对我来说始终有效的最佳解决方案是使用 Multimap 和 TreeMultiMap。即使您有多个重复的键,这也会按键的升序排列结果。解决方法如下:

        Multimap<Double, Integer> map= TreeMultimap.create(Ordering.natural().reverse(),         Ordering.natural());
        
        if (!map.isEmpty()) {               
                        printMap(map);
                    }
        
        public static <K, V> void printMap(Multimap<Double, Integer> map) throws Exception {
                for (Map.Entry<Double, Integer> entry : map.entries()) {
                    System.out.println("Key : " + entry.getKey() 
                        + " Value : " + entry.getValue());              
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-22
          • 2015-12-13
          • 1970-01-01
          • 2010-10-29
          相关资源
          最近更新 更多