使Multimap仅在Java中按键排序

我想要一个仅基于键排序的cgccMultimap 。 不应对值进行排序。 我试图用番石榴的TreeMultimap构建一些东西,但我不能使用它,因为值类型没有实现Comparable

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

我已经考虑过获取一个密钥的SortedSet ,然后遍历排序集中的每个密钥以获取各种值,但我希望在Guava中使用现有的(但尚未发现的)function而不是使用这种hack

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


输入/输出示例:

 Set 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 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 

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

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

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

MultimapBuilder在Guava 16中引入:

 , V> ListMultimap multimap() { return MultimapBuilder.treeKeys().linkedListValues().build(); } 

这会使您的密钥按其自然顺序排序( treeKeys()也会重载以接受自定义比较器),并且与每个密钥关联的值都保存在LinkedListArrayListHashSet是其他选项之一)。

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

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

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

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

这个怎么样:

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

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

如果使用Comparators,可以使用TreeMultimap执行此操作 。

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

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

总是适合我的最佳解决方案是使用Multimap和TreeMultiMap。 即使您有多个重复键,这也会按键按升序排序。 方案如下:

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