web-dev-qa-db-ja.com

HashMapのリストに追加するためのショートカット

オブジェクトのリストを取得し、オブジェクトに含まれる値に基づいてそれらをマップにグループ化する必要があることがよくあります。例えば。国別のユーザーとグループのリストを取得します。

このための私のコードは通常次のようになります。

Map<String, List<User>> usersByCountry = new HashMap<String, List<User>>();
for(User user : listOfUsers) {
    if(usersByCountry.containsKey(user.getCountry())) {
        //Add to existing list
        usersByCountry.get(user.getCountry()).add(user);

    } else {
        //Create new list
        List<User> users = new ArrayList<User>(1);
        users.add(user);
        usersByCountry.put(user.getCountry(), users);
    }
}

しかし、私はこれが厄介であり、一部の教祖はより良いアプローチを持っていると考えることを余儀なくされます。これまでのところ、最も近いのは Google CollectionsのMultiMap です。

標準的なアプローチはありますか?

ありがとう!

50
Damo

Java 8では、 Map#computeIfAbsent() を使用できます。

_Map<String, List<User>> usersByCountry = new HashMap<>();

for (User user : listOfUsers) {
    usersByCountry.computeIfAbsent(user.getCountry(), k -> new ArrayList<>()).add(user);
}
_

または、Stream APIの Collectors#groupingBy() を使用して、ListからMapに直接移動します。

_Map<String, List<User>> usersByCountry = listOfUsers.stream().collect(Collectors.groupingBy(User::getCountry));
_

Java 7以下では、次のものが得られます:

_Map<String, List<User>> usersByCountry = new HashMap<>();

for (User user : listOfUsers) {
    List<User> users = usersByCountry.get(user.getCountry());
    if (users == null) {
        users = new ArrayList<>();
        usersByCountry.put(user.getCountry(), users);
    }
    users.add(user);
}
_

Commons Collections には LazyMap がありますが、パラメータ化されていません。 Guava にはLazyMapLazyListのようなものはありませんが、このように Multimap を使用できますin 以下の多剤の回答

67
BalusC

グアバの Multimap は実際にこれに最も適したデータ構造であり、実際には Multimaps.index(Iterable<V>, Function<? super V,K>) ユーティリティメソッドがあります。あなたが望む: Iterable<V> (これは List<V> である)を取り、 Function<? super V, K> を適用するMultimap<K,V>のキーを取得します。

ドキュメントの例を次に示します。

例えば、

  List<String> badGuys
      = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
  Function<String, Integer> stringLengthFunction = ...;
  Multimap<Integer, String> index
      = Multimaps.index(badGuys, stringLengthFunction);
  System.out.println(index);

プリント

 {4=[Inky], 5=[Pinky, Pinky, Clyde], 6=[Blinky]}

あなたの場合、あなたはFunction<User,String> userCountryFunction = ...を書くでしょう。

20

これを何度も行うようですので、テンプレートクラスを作成しました

public abstract class ListGroupBy<K, T> {
public Map<K, List<T>> map(List<T> list) {
    Map<K, List<T> > map = new HashMap<K, List<T> >();
    for (T t : list) {
        K key = groupBy(t);
        List<T> innerList = map.containsKey(key) ? map.get(key) : new ArrayList<T>();
        innerList.add(t);
        map.put(key, innerList);
    }
    return map;
}

protected abstract K groupBy(T t);
}

GroupByの実装を提供するだけです

あなたの場合

String groupBy(User u){return user.getCountry();}
2
Jukey

コレクション値のマップを処理しなければならないとき、私はほとんど常にクラスに小さなputIntoListMap()静的ユーティリティメソッドを書くことになります。複数のクラスで必要な場合は、そのメソッドをユーティリティクラスにスローします。そのような静的メソッド呼び出しは少しいですが、毎回コードを入力するよりもずっときれいです。アプリでマルチマップがかなり中心的な役割を果たしていない限り、別の依存関係を取り込むことはおそらく価値がありません。

2
Luke Maurer

lambdaj を使用すると、次のように1行のコードで結果を取得できます。

Group<User> usersByCountry = group(listOfUsers, by(on(User.class).getCountry()));

Lambdajは、非常に読みやすいドメイン固有の言語でコレクションを操作するためのその他の多くの機能も提供します。

2
Mario Fusco

GCライブラリの LinkedHashMultimap で正確なニーズが満たされているようです。依存関係に対応できる場合、すべてのコードは次のようになります。

SetMultimap<String,User> countryToUserMap = LinkedHashMultimap.create();
// .. other stuff, then whenever you need it:
countryToUserMap.put(user.getCountry(), user);

挿入順序は維持され(リストで行っているように見えるすべてについて)、重複は排除されます。もちろん、必要に応じてプレーンハッシュベースのセットまたはツリーセットに切り替えることができます(またはリストですが、それは必要なものではないようです)。ユーザーのいない国を要求したり、全員がポニーを取得したりすると、空のコレクションが返されます。つまり、APIをチェックしてください。それはあなたのために多くのことをするので、依存関係はそれの価値があるかもしれません。

1
Carl

要素を追加するクリーンで読みやすい方法は次のとおりです。

String country = user.getCountry();
Set<User> users
if (users.containsKey(country))
{
    users = usersByCountry.get(user.getCountry());
}
else
{
    users = new HashSet<User>();
    usersByCountry.put(country, users);
}
users.add(user);

containsKeygetの呼び出しは、単にgetを呼び出してnullの結果をテストするよりも遅くないことに注意してください。

0
starblue
Map<String, List<User>> usersByCountry = new HashMap<String, List<User>>();
for(User user : listOfUsers) {
    List<User> users = usersByCountry.get(user.getCountry());
    if (users == null) {        
        usersByCountry.put(user.getCountry(), users = new ArrayList<User>());
    }
    users.add(user);
}
0
user1529313