使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.index
… ImmutableListMultimap
保持事物的顺序与给定它们的顺序相同。
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()
也会重载以接受自定义比较器),并且与每个密钥关联的值都保存在LinkedList
( ArrayList
和HashSet
是其他选项之一)。
虽然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()); } }