私は、単純なJavaのList
をY
- > Z
から "変換"する方法を知っています。
List<String> x;
List<Integer> y = x.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
さて、私は基本的に地図でも同じことをしたいのです。
INPUT:
{
"key1" -> "41", // "41" and "42"
"key2" -> "42 // are Strings
}
OUTPUT:
{
"key1" -> 41, // 41 and 42
"key2" -> 42 // are Integers
}
解決策はString
- > Integer
に限定されるべきではありません。上記のList
の例と同じように、任意のメソッド(またはコンストラクター)を呼び出したいです。
Map<String, String> x;
Map<String, Integer> y =
x.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getKey(),
e -> Integer.parseInt(e.getValue())
));
それはリストコードほど素敵ではありません。 map()
呼び出しで新しいMap.Entry
sを作成することはできません。そのため、作業はcollect()
呼び出しに混在します。
これは Sotirios Delimanolisの答え に対するいくつかのバリエーションです。これは(+1)から始めるとかなり良かったです。次の点を考慮してください。
static <X, Y, Z> Map<X, Z> transform(Map<? extends X, ? extends Y> input,
Function<Y, Z> function) {
return input.keySet().stream()
.collect(Collectors.toMap(Function.identity(),
key -> function.apply(input.get(key))));
}
カップルがここを指しています。 1つ目は、総称でのワイルドカードの使用です。これにより、機能が多少柔軟になります。たとえば、入力マップのキーのスーパークラスであるキーを出力マップに含める場合は、ワイルドカードが必要になります。
Map<String, String> input = new HashMap<String, String>();
input.put("string1", "42");
input.put("string2", "41");
Map<CharSequence, Integer> output = transform(input, Integer::parseInt);
(マップの値の例もありますが、実際には工夫されています。Yの境界ワイルドカードを使用することは、Edgeの場合にのみ役立ちます。)
2つ目のポイントは、入力マップのentrySet
の上でストリームを実行するのではなく、keySet
の上でそれを実行したということです。これにより、マップエントリからではなくマップから値をフェッチする必要があるという犠牲を払って、コードが少しきれいになります。ちなみに、最初はtoMap()
への最初の引数としてkey -> key
を持っていましたが、これは何らかの理由で型推論エラーで失敗しました。 Function.identity()
と同様に(X key) -> key
に変更してもうまくいきました。
さらに別の変形は以下の通りである。
static <X, Y, Z> Map<X, Z> transform1(Map<? extends X, ? extends Y> input,
Function<Y, Z> function) {
Map<X, Z> result = new HashMap<>();
input.forEach((k, v) -> result.put(k, function.apply(v)));
return result;
}
これはストリームの代わりにMap.forEach()
を使います。これはさらに簡単です、それは私が思う、それはそれが地図で使用するために多少不器用であるコレクターを不要にするので。その理由は、Map.forEach()
がキーと値を別々のパラメータとして与えるのに対して、ストリームは1つの値しか持たない - そしてその値としてキーとマップエントリのどちらを使うかを選択しなければならないからです。マイナス面では、これは他のアプローチの豊かで流線形の良さを欠いています。 :-)
そのような一般的な解決策
public static <X, Y, Z> Map<X, Z> transform(Map<X, Y> input,
Function<Y, Z> function) {
return input
.entrySet()
.stream()
.collect(
Collectors.toMap((entry) -> entry.getKey(),
(entry) -> function.apply(entry.getValue())));
}
例
Map<String, String> input = new HashMap<String, String>();
input.put("string1", "42");
input.put("string2", "41");
Map<String, Integer> output = transform(input,
(val) -> Integer.parseInt(val));
それは絶対に100%機能的で流暢である必要がありますか?そうでなければ、これはどうですか、それはそれが得るのと同じくらい短いです:
Map<String, Integer> output = new HashMap<>();
input.forEach((k, v) -> output.put(k, Integer.valueOf(v));
Guavaの関数 Maps.transformValues
はあなたが探しているもので、ラムダ式でうまく働きます。
Maps.transformValues(originalMap, val -> ...)
標準のストリームAPIを強化するMy StreamEx ライブラリは、適切な EntryStream
クラスを提供します。マップを変換するのに適しています。
Map<String, Integer> output = EntryStream.of(input).mapValues(Integer::valueOf).toMap();
ToMap()JDKコレクターはここでは簡潔です(+1 ここで )。
Map<String,Integer> newMap = givenMap.
entrySet().
stream().collect(Collector.of
( ()-> new HashMap<String,Integer>(),
(mutableMap,entryItem)-> mutableMap.put(entryItem.getKey(),Integer.parseInt(entryItem.getValue())),
(map1,map2)->{ map1.putAll(map2); return map1;}
));
サードパーティのライブラリを使用しても構わない場合は、my cyclops-react libにすべての拡張機能があります JDK CollectionMap などの型。 'map'演算子を使用して直接マップを変換することができます(デフォルトでは、マップはマップ内の値に作用します)。
MapX<String,Integer> y = MapX.fromMap(HashMaps.of("hello","1"))
.map(Integer::parseInt);
bimapはキーと値を同時に変換するために使うことができます
MapX<String,Integer> y = MapX.fromMap(HashMaps.of("hello","1"))
.bimap(this::newKey,Integer::parseInt);
宣言的で簡単な解決策は次のようになります。
yourMutableMap.replaceAll((key、val) - > return_value_of_bi_your_function); Nb。マップの状態を変更することに注意してください。だからこれはあなたが望むものではないかもしれません。
乾杯: http://www.deadcoderising.com/2017-02-14-Java-8-declarative-ways-of-modifying-a-map-using-compute-merge- / と置き換えます