Сортировка Multimap по ключам только в Java - PullRequest
22 голосов
/ 31 марта 2011

Я хотел бы иметь 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

Ответы [ 8 ]

21 голосов
/ 31 марта 2011

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 для значений.

16 голосов
/ 10 июля 2014

MultimapBuilder введено в Guava 16:

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

При этом ваши ключи сортируются по их естественному порядку (treeKeys() также перегружен для принятия пользовательского компаратора), а значения, связанные с каждым ключом, сохраняются в LinkedList (ArrayList и HashSet относятся другие варианты).

8 голосов
/ 04 ноября 2011

Хотя на конкретную ситуацию ОП, похоже, ответили с помощью неизменяемых функций построения нескольких карт, мне понадобилась изменяемая версия того, о чем он просил.В случае, если это кому-нибудь поможет, вот общий метод, который я в итоге создал:

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);
            }
        });
}
4 голосов
/ 05 апреля 2011

Вызов Multimaps.newMultimap , который дает вам гибкость для создания, например, Multimap, поддерживаемого TreeMap, значения которого - ArrayLists.

2 голосов
/ 16 апреля 2013

Я хотел бы отметить, что альтернативное предлагаемое решение, а именно "для создания TreeMultimap и использования Ordering.arbitrary () в качестве компаратора для значений" , работает, только если MyObject непереопределить equals () или hashcode ().Ordering.arbitrary () не согласуется с equals и использует вместо этого идентификатор объекта, что делает нецелесообразным использование его в сочетании с TreeSet.

1 голос
/ 31 марта 2011

Как насчет этого:

    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.

1 голос
/ 31 марта 2011

Вы можете сделать это с TreeMultimap , если вы используете компараторы.

Создайте Comparator для типа ключа и типа значения (MyObject?). Затем используйте create (Comparator keyComparator, Comparator valueComparator) для создания карты.

Преимущество использования Comparator по сравнению с реализацией Comparable заключается в том, что вы можете сделать Comparator специфичным для ситуации, которую вы хотите использовать на карте, и это не повлияет на ваш объект в целом. Пока ваш Comparator соответствует с равными, он может делать все, что вы хотите.

0 голосов
/ 19 сентября 2018

Лучшее решение, которое всегда работает для меня, это использовать 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());              
        }
    }
...