ต่อไปนี้เป็นคำตอบบางส่วนของคำตอบของ 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))));
}
สองสามคะแนนที่นี่ ครั้งแรกคือการใช้สัญลักษณ์แทนใน generics; ทำให้ฟังก์ชั่นค่อนข้างยืดหยุ่น คุณจำเป็นต้องใช้สัญลักษณ์แทนหากคุณต้องการให้แผนที่เอาท์พุทมีคีย์ที่เป็นซูเปอร์คลาสของคีย์แผนที่ของอินพุต:
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 จะช่วยในกรณีที่เป็นขอบเท่านั้น)
จุดที่สองคือแทนที่จะวิ่งกระแสมากกว่าแผนที่การป้อนข้อมูลของฉันวิ่งมันมากกว่า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()
ให้คีย์และค่าเป็นพารามิเตอร์แยกขณะที่กระแสมีเพียงค่าเดียว - และคุณต้องเลือกว่าจะใช้คีย์หรือรายการแผนที่เป็นค่านั้น ในด้านลบนี้ขาดความอุดมสมบูรณ์และความดีงามของวิธีการอื่น ๆ :-)
e -> e.getKey()
Map.Entry::getKey
แต่นั่นเป็นเรื่องของรสนิยม / รูปแบบการเขียนโปรแกรม