(+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に制限されたワイルドカードを使用することは、エッジケースでのみ役立つことを認めます。)
2番目のポイントは、入力マップのを介してストリームを実行する代わりに、を介してストリームを実行entrySetしたことkeySetです。これにより、コードが少しクリーンになりますが、マップエントリからではなくマップから値をフェッチする必要があります。ちなみに、私は最初にkey -> key最初の引数として持っていましたがtoMap()、これは何らかの理由で型推論エラーで失敗しました。機能するように変更しまし(X key) -> keyたFunction.identity()。
さらに別のバリエーションは次のとおりです。
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つしかないのに対し、キーと値は別々のパラメーターとして与えられるためです。その値としてキーとマップエントリのどちらを使用するかを選択する必要があります。マイナス面として、これには他のアプローチの豊かで流麗な良さが欠けています。:-)
               
              
e -> e.getKey()てMap.Entry::getKey。しかし、それは好み/プログラミングスタイルの問題です。