25

我想要一个c.g.c.c.Multimap仅基于键排序的。这些值不应该被排序。我试图用 guava's 构建一些东西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
4

8 回答 8

22

Multimaps.index返回 an ImmutableListMultimap,因此您将无法在创建后对其进行排序。但是,您可以首先创建您的排序副本Iterable<MyObject>并将其提供给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());
}

另一种选择可能是创建一个TreeMultimapOrdering.arbitrary()用作Comparator值。

于 2011-03-31T15:07:32.260 回答
17

MultimapBuilder在 Guava 16 中引入:

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

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

于 2014-07-10T11:08:10.767 回答
8

虽然 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);
            }
        });
}
于 2011-11-03T23:33:59.330 回答
4

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

于 2011-04-04T20:42:13.293 回答
2

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

于 2013-04-16T13:50:42.627 回答
1

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

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

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

于 2011-03-31T14:42:35.227 回答
1

这个怎么样:

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

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

于 2011-03-31T14:57:12.783 回答
0

始终对我有用的最佳解决方案是使用 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());              
        }
    }
于 2018-09-19T18:35:03.467 回答