web-dev-qa-db-ja.com

ストリームを使用して、HashMapで値をマップするにはどうすればよいですか?

PersonにString getName()(etc)メソッドが含まれる_Map<String, Person>_が指定されている場合、_Map<String, Person>_を_Map<String, String>_に変換するにはどうすればよいですかStringPerson::getName()

使用するJava 8より前

_Map<String, String> byNameMap = new HashMap<>();

for (Map.Entry<String, Person> person : people.entrySet()) {
    byNameMap.put(person.getKey(), person.getValue().getName());
}
_

しかし、私はストリームとラムダを使用してそれをしたいと思います。

これを機能的なスタイルで行う方法がわかりません:Map/HashMapは Stream を実装していません。

people.entrySet()は、ストリーミングできる_Set<Entry<String, Person>>_を返しますが、新しい_Entry<String, String>_を宛先マップに追加するにはどうすればよいですか?

31
David Kerr

Java 8を使用すると、次のことができます。

Map<String, String> byNameMap = new HashMap<>();
people.forEach((k, v) -> byNameMap.put(k, v.getName());

Guavaの Maps.transformValues を使用したほうがよいでしょう。これは、元のMapをラップし、getを実行するときに変換を実行します。つまり、実際に値を消費したときの変換コスト。

Guavaを使用すると、次のようになります。

Map<String, String> byNameMap = Maps.transformValues(people, Person::getName);

編集:

@Eelcoのコメントに続いて(そして完全を期すため)、マップへの変換は Collectors.toMap を使用して以下のように行うとより効果的です。

Map<String, String> byNameMap = people.entrySet()
  .stream()
  .collect(Collectors.toMap(Map.Entry::getKey, (entry) -> entry.getValue().getName());
29
Nick Holt

1つの方法は、toMapコレクターを使用することです。

import static Java.util.stream.Collectors.toMap;

Map<String, String> byNameMap = people.entrySet().stream()
                                     .collect(toMap(Entry::getKey, 
                                                    e -> e.getValue().getName()));
19
assylias

悲しいことに、手元にあるライブラリーでは見つけられなかった少し一般的なコードを使用する

public static <K, V1, V2> Map<K, V2> remap(Map<K, V1> map,
        Function<? super V1, ? extends V2> function) {

    return map.entrySet()
            .stream() // or parallel
            .collect(Collectors.toMap(
                    Map.Entry::getKey, 
                    e -> function.apply(e.getValue())
                ));
}

これは本質的にGuavas Maps.transformValuesから他の人が言及するマイナス面を差し引いたものと同じになります。

Map<String, Person> persons = ...;
Map<String, String> byNameMap = remap(persons, Person::getName);

そして、リマッピング関数のキーと値が必要な場合、この2番目のバージョンはそれを可能にします

public static <K, V1, V2> Map<K, V2> remap(Map<K, V1> map,
        BiFunction<? super K, ? super V1, ? extends V2> function) {

    return map.entrySet()
            .stream() // or parallel
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    e -> function.apply(e.getKey(), e.getValue())
                ));
}

それは例えばのように使用することができます

Map<String, String> byNameMap = remap(persons, (key, val) -> key + ":" + val.getName());
1
zapl