Tener un Multimap ordenado en claves solo en Java
sorting guava (8)
Aunque la situación específica del OP parece haber sido respondida utilizando funciones de creación de múltiples mapas inmutables, necesitaba una versión mutable de lo que estaba pidiendo. En caso de que ayude a alguien, aquí está el método genérico que terminé creando:
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);
}
});
}
Me gustaría tener un cgccMultimap
que se cgccMultimap
solo en base a claves. Los valores no deben ser ordenados. He intentado construir algo con el TreeMultimap
de TreeMultimap
, pero no puedo usarlo porque el tipo de valor no implementa 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;
}
}
He pensado en obtener un SortedSet
de claves SortedSet
, luego iterar sobre cada una de estas claves en el conjunto ordenado para obtener los distintos valores, pero esperaba utilizar una característica existente (aún sin descubrir) en Guava en lugar de usar este tipo de pirateo. .
Nota: No haré que MyObject
implemente Comparable
porque no tiene sentido con mi objeto real.
Ejemplo de entrada / salida:
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());
}
Huellas dactilares:
1 -> c // or switched with line below
1 -> e
2 -> a
3 -> b // or switched with line below
3 -> d
La mejor solución que siempre me funciona es usar Multimap y TreeMultiMap. esto ordenará los resultados en orden ascendente en las teclas, incluso si tiene varias teclas duplicadas. Solución a continuación:
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());
}
}
Llame a Multimaps.newMultimap , que le brinda la flexibilidad de crear, por ejemplo, un Multimap respaldado por TreeMap cuyos valores son ArrayLists.
Me gustaría señalar que la solución alternativa propuesta, a saber, "crear un TreeMultimap y usar Ordering.arbitrary () como el comparador para los valores" , solo funciona si MyObject no anula equals () o hashcode (). Ordering.arbitrary () es inconsistente con los iguales y utiliza la identidad del objeto, lo que hace que no sea una buena idea usarlo junto con un TreeSet.
Puedes hacerlo con TreeMultimap si usas Comparadores.
Cree un Comparator para el tipo de clave y el tipo de valor (¿ MyObject
?). Luego use create (Comparator keyComparator, Comparator valueComparator) para hacer el mapa.
La ventaja de usar un Comparator sobre la implementación de Comparable es que puede hacer que el Comparator sea específico para la situación que desee con el mapa y no afecte su objeto en general. Mientras su Comparador sea consistente con iguales, puede hacer lo que usted quiera.
Qué tal esto:
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;
}
Sin embargo, en este caso habría la sobrecarga de crear dos Multimap
s separados.
MultimapBuilder
fue introducido en Guava 16:
<K extends Comparable<? super K>, V> ListMultimap<K, V> multimap() {
return MultimapBuilder.treeKeys().linkedListValues().build();
}
Eso mantiene sus claves ordenadas por su orden natural ( treeKeys()
también está sobrecargada para aceptar un comparador personalizado), y los valores asociados con cada clave se mantienen en una LinkedList
( ArrayList
y HashSet
encuentran entre otras opciones).
Multimaps.index
devuelve un ImmutableListMultimap
, por lo que no podrá clasificarlo después de crearlo. Sin embargo, primero puede crear una copia ordenada de su Iterable<MyObject>
a Multimap.index
... ImmutableListMultimap
mantiene las cosas en el mismo orden en que se las dieron.
public static ImmutableMultimap<Integer, MyObject> indexOnScore(Iterable<MyObject> i) {
List<MyObject> sorted = Ordering.natural().onResultOf(myObjectToScore())
.sortedCopy(i);
return Multimaps.index(sorted, myObjectToScore());
}
Otra opción podría ser crear un TreeMultimap
y usar Ordering.arbitrary()
como el Comparator
para los valores.