web-dev-qa-db-ja.com

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を取得し、並べ替えられたセット内のこれらの各キーを繰り返し処理してさまざまな値を取得することを考えましたが、Guavaの既存の(まだ発見されていない)機能を使用することを望んでいました。この種の使用ハック

注:実際のオブジェクトでは意味がないため、MyObjectComparableを実装させることはしません。


入出力の例:

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
25

MultimapBuilder Guava 16で導入されました:

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

これにより、キーが自然な順序で並べ替えられたままになり(treeKeys()もオーバーロードされてカスタムコンパレータを受け入れます)、各キーに関連付けられた値はLinkedListArrayListHashSetは他のオプションの中にあります)。

16
gdejohn

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);
            }
        });
}
8
Trevor Robinson

Multimaps.newMultimap を呼び出します。これにより、たとえば、値がArrayListsであるTreeMapに基づくマルチマップを柔軟に作成できます。

4
Jared Levy

提案された代替ソリューション、つまり"TreeMultimapを作成し、Ordering.arbitrary()を値のコンパレータとして使用する"は、MyObjectがequalsをオーバーライドしない場合にのみ機能することを指摘しておきます。 ()またはhashcode()。 Ordering.arbitrary()はequalsと矛盾しており、代わりにオブジェクトIDを使用するため、TreeSetと組み合わせて使用​​することはお勧めできません。

2
GFonte

これはどう:

    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;
    }

ただし、この場合、2つの別々のMultimapを作成するオーバーヘッドが発生します。

1
Paul Blessing

コンパレータを使用する場合は、 TreeMultimap で実行できます。

キータイプと値タイプ(MyObject?)に対して コンパレータ を作成します。次に、 create(Comparator keyComparator、Comparator valueComparator) を使用してマップを作成します。

Comparableを実装するよりもComparatorを使用する利点は、Comparatorをマップで必要な状況に固有にすることができ、一般にオブジェクトに影響を与えないことです。あなたのコンパレータがequalsと一致している限り、それはあなたが望むことを何でもすることができます。

1
justkt

私にとって常に有効な最善の解決策は、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());              
        }
    }
0
Rahul k