我想要一个仅基于密钥排序的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
答案 0 :(得分:21)
Multimaps.index
会返回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());
}
另一种选择可能是创建TreeMultimap
并使用Ordering.arbitrary()
作为值的Comparator
。
答案 1 :(得分:16)
MultimapBuilder
在Guava 16中引入:
<K extends Comparable<? super K>, V> ListMultimap<K, V> multimap() {
return MultimapBuilder.treeKeys().linkedListValues().build();
}
这会使您的密钥按其自然顺序排序(treeKeys()
也会重载以接受自定义比较器),并且与每个密钥关联的值都保存在LinkedList
(ArrayList
和HashSet
是其他选项之一。)
答案 2 :(得分: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);
}
});
}
答案 3 :(得分:4)
调用Multimaps.newMultimap,这使您可以灵活地创建,例如,由TreeMap支持的Multimap,其值为ArrayLists。
答案 4 :(得分:2)
我想指出另一种提议的解决方案,即“创建一个TreeMultimap并使用Ordering.arbitrary()作为值”的比较器,只有在MyObject没有的情况下才有效t覆盖equals()或hashcode()。 Ordering.arbitrary()与equals不一致,而是使用对象标识,这使得将它与TreeSet结合使用并不是一个好主意。
答案 5 :(得分:1)
如果您使用比较器,则可以使用TreeMultimap执行此操作。
为密钥类型和值类型(MyObject
?)创建Comparator。然后使用create(Comparator keyComparator, Comparator valueComparator)制作地图。
使用Comparator而不是实现Comparable的好处是,您可以使Comparator特定于您想要的地图情况,并且它通常不会影响您的对象。只要你的比较器与equals一致,它就可以做你想做的任何事情。
答案 6 :(得分: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
会产生开销。
答案 7 :(得分: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());
}
}