本文主要讲述Java8 Map的merger操作。
//计算唯一的单词出现次数
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;
}
}