Подтвердить что ты не робот

Наличие Multimap, отсортированного по ключам только в Java

Я хотел бы иметь c.g.c.c.Multimap, который сортируется только на основе ключей. Значения не должны сортироваться. Я пытался создать что-то с guava 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 ключей, а затем повторил по каждому из этих ключей в отсортированном наборе для получения различных значений, но я надеялся использовать существующую (но неоткрытую) функцию в Гуаве, а не используя этот вид взлома.

Примечание. Я не буду использовать 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
4b9b3361

Ответ 1

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

Ответ 2

MultimapBuilder был введен в Guava 16:

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

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

Ответ 3

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

    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.