JAVA知识回顾之Java8 Merge
0x00 概述
本文主要讲述Java8 Map的merger操作。
0x01 用例和代码
//计算唯一的单词出现次数 var map = new HashMap<String, Integer>(); words.forEach(word -> { var prev = map.get(word); if (prev == null) { map.put(word, 1); } else { map.put(word, prev + 1); } });
//给定输入 对应的输出结果 var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz"); //... {Bar=1, Fizz=2, Foo=3, Buzz=2}
//进行重构以避免条件逻辑 words.forEach(word -> { map.putIfAbsent(word, 0); map.put(word, map.get(word) + 1); });
putIfAbsent()
是必要的,否则代码会在第一次出现未知的单词时中断,另外map.get(word)
里面map.put()
有点尴尬。words.forEach(word -> { map.putIfAbsent(word, 0); map.computeIfPresent(word, (w, prev) -> prev + 1); });
仅当question(word
)中的键存在时才调用给定的转换。否则它什么都不做。
words.forEach(word -> map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1) );
compute()
就像是computeIfPresent(),
无论给定key是否存在都会调用它。如果键的值不存在,则prev
参数为null。
实现一个简单的merge:
default V merge(K key, V value, BiFunction<V, V, V> remappingFunction) { V oldValue = get(key); V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); if (newValue == null) { remove(key); } else { put(key, newValue); } return newValue; }
merge()
适用于两种情况。如果给定的Key值不存在,它就变成了put(key, value)
。但如果所述Key已经存值,remappingFunction
可以操作合并:
- 只需返回新值覆盖旧值:
(old, new) -> new
- 只需返回旧值保留旧值:
(old, new) -> old
- 以某种方式合并两者,例如:
(old, new) -> old + new
- 甚至删除旧值:
(old, new) -> null
//1 的下word ,如果没有key就将1添加到现有值。 words.forEach(word -> map.merge(word, 1, (prev, one) -> prev + one) );
使用merge()模拟一个帐户操作:
//vo class Operation { private final String accNo; private final BigDecimal amount; } //针对不同账户的操作 var operations = List.of( new Operation("123", new BigDecimal("10")), new Operation("456", new BigDecimal("1200")), new Operation("123", new BigDecimal("-4")), new Operation("123", new BigDecimal("8")), new Operation("456", new BigDecimal("800")), new Operation("456", new BigDecimal("-1500")), new Operation("123", new BigDecimal("2")), new Operation("123", new BigDecimal("-6.5")), new Operation("456", new BigDecimal("-600")) );
如果想要为每个账户计算余额,不使用merge:
var balances = new HashMap<String, BigDecimal>(); operations.forEach(op -> { var key = op.getAccNo(); balances.putIfAbsent(key, BigDecimal.ZERO); balances.computeIfPresent(key, (accNo, prev) -> prev.add(op.getAmount())); });
使用merge():
operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), (soFar, amount) -> soFar.add(amount)) ); operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add) ); //{123=9.5, 456=-100}
完整代码:
package other; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import org.junit.Test; import java.util.*; /** * @author liuqian * @date 2019/10/7 19:11. */ public class MapMethodsTest { @Test public void mapMergeTest() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); List<StudentScore> studentScoreList = buildATestList(); // 按照学生分组,求得每个学生的总分 // 常规做法 Map<String, Integer> studentScoreMap = new HashMap<>(); studentScoreList.forEach(studentScore -> { if (studentScoreMap.containsKey(studentScore.getStuName())) { studentScoreMap.put(studentScore.getStuName(), studentScoreMap.get(studentScore.getStuName()) + studentScore.getScore()); } else { studentScoreMap.put(studentScore.getStuName(), studentScore.getScore()); } }); // {"李四":228,"张三":215,"王五":235} System.out.println(objectMapper.writeValueAsString(studentScoreMap)); // merge() 方法 Map<String, Integer> studentScoreMap2 = new HashMap<>(); studentScoreList.forEach(studentScore -> studentScoreMap2.merge( studentScore.getStuName(), studentScore.getScore(), Integer::sum)); // {"李四":228,"张三":215,"王五":235} System.out.println(objectMapper.writeValueAsString(studentScoreMap2)); } @Test public void mapComputeTest() { String k = "key"; Map<String, Integer> map = new HashMap<String, Integer>() {{ put(k, 1); }}; // 2 System.out.println(map.compute(k, (key, oldVal) -> oldVal + 1)); } @Test public void mapCountComputeTest() { List<String> words = new ArrayList<String>() {{ add("A"); add("B"); add("C"); add("A"); add("C"); add("E"); add("E"); add("E"); add("E"); add("A"); add("E"); }}; // the word appear times Map<String, Integer> wordCountMap = new HashMap<>(); words.forEach(word -> wordCountMap.compute(word, (key, oldCount) -> { if (Objects.isNull(oldCount)) { return 1; } return oldCount + 1; })); System.out.println(wordCountMap); } private List<StudentScore> buildATestList() { List<StudentScore> studentScoreList = new ArrayList<>(); StudentScore studentScore1 = new StudentScore() {{ setStuName("张三"); setSubject("语文"); setScore(70); }}; StudentScore studentScore2 = new StudentScore() {{ setStuName("张三"); setSubject("数学"); setScore(80); }}; StudentScore studentScore3 = new StudentScore() {{ setStuName("张三"); setSubject("英语"); setScore(65); }}; StudentScore studentScore4 = new StudentScore() {{ setStuName("李四"); setSubject("语文"); setScore(68); }}; StudentScore studentScore5 = new StudentScore() {{ setStuName("李四"); setSubject("数学"); setScore(70); }}; StudentScore studentScore6 = new StudentScore() {{ setStuName("李四"); setSubject("英语"); setScore(90); }}; StudentScore studentScore7 = new StudentScore() {{ setStuName("王五"); setSubject("语文"); setScore(80); }}; StudentScore studentScore8 = new StudentScore() {{ setStuName("王五"); setSubject("数学"); setScore(85); }}; StudentScore studentScore9 = new StudentScore() {{ setStuName("王五"); setSubject("英语"); setScore(70); }}; studentScoreList.add(studentScore1); studentScoreList.add(studentScore2); studentScoreList.add(studentScore3); studentScoreList.add(studentScore4); studentScoreList.add(studentScore5); studentScoreList.add(studentScore6); studentScoreList.add(studentScore7); studentScoreList.add(studentScore8); studentScoreList.add(studentScore9); return studentScoreList; } @Data private class StudentScore { private String stuName; private String subject; private Integer score; } }