任务是这样的,想要通过数据库查到两张表的数据,然后通过关联的id给对象赋值,再通过对象的某个字段进行分组,最终放入大对象中。
放在下面就是获取People
和Position
,然后通过id,将对应的职位放入People
中,分组的结果放入PositionObj
构建的列表中。
相关代码如下所示,这里模拟了数据库的查询。
public class MergeAlorithm {
public static void main(String[] args) {
// 先通过设置map 再通过map给对象设值 然后通过该对象 进行分组 放入大对象中
List<Position> positiones = getPositiones();
Map<Integer, String> map = positiones.stream().collect(Collectors.toMap(Position::getId, Position::getPosition));
List<People> peoples = getPeopleList();
// 补充代码
}
// 假装是从db中获取的people列表
public static List<People> getPeopleList() {
return Lists.newArrayList(
new People(4, "小a", null),
new People(2, "小b", null),
new People(2, "小c", null),
new People(6, "小d", null)
);
}
// 假装是从db中获取的职位列表
public static List<Position> getPositiones() {
return Lists.newArrayList(
new Position(1, "班长"),
new Position(2, "小组长"),
new Position(3, "委员")
);
}
}
@Data
@AllArgsConstructor
class People {
private Integer id;
private String name;
private String position;
}
@Data
@AllArgsConstructor
class Position {
private Integer id;
private String position;
}
@Data
@AllArgsConstructor
class PositionObj {
private String position;
private List<People> peoples;
}
通过循环设值,然后通过流分组,最后设置对象,看似没什么问题,但是多了一次循环,能不能再优化呢?
// 1. 设置 外层再分组
// 循环 通过map 设值 总共三次循环
for (People people : peoples) {
String position = map.getOrDefault(people.getId(), "无");
people.setPosition(position);
}
// 通过流 再遍历
Map<String, List<People>> positionMap = peoples.stream().collect(Collectors.groupingBy(People::getPosition));
//再放到一个对象中
List<PositionObj> positionObjs = positionMap.entrySet().stream().map(entry -> new PositionObj(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
System.out.println(JSON.toJSON(positionObjs));
可以看到,我们在第一次循环中,直接进行了分组,而使用computeIfAbsent
可以先检查对应的k是否在map中,存在则关联对应的列表,放入people,如果不存在则创建新列表放入people,完美符合我们的要求。
positionMap.computeIfAbsent(position, k -> new ArrayList<>()).add(people);
关键就是这一行代码
// 2.减少循环 借助map方法
// 循环 通过map 设值
Map<String, List<People>> positionMap = new HashMap<>();
for (People people : peoples) {
String position = map.getOrDefault(people.getId(), "无");
people.setPosition(position);
//通过职位分组
//computeIfAbsent 方法首先检查 position 是否已经在 positionMap 中存在。
//如果 position 存在,它返回与该键关联的列表,并将 people 添加到该列表中。
//如果 position 不存在,它执行计算函数(即 lambda 表达式 k -> new ArrayList<>()),创建一个新的空列表,并将其与 position 关联。
//最后,它将 people 添加到新创建的列表中。
positionMap.computeIfAbsent(position, k -> new ArrayList<>()).add(people);
}
//再放到一个对象中
List<PositionObj> positionObjs = positionMap.entrySet().stream().map(entry -> new PositionObj(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
System.out.println(JSON.toJSON(positionObjs));
但是这个方法我之前是没怎么用到的,还有一些其他方法其实都不大熟悉,所以我想着不如来整理下Map的那些复杂的方法吧。
方法接收两个参数 getOrDefault(key,default)
,如果能通过key获取值则返回该value,如果不能,则返回默认值,这个常见与我们需要得到一些默认值,防止空值引发的空指针计算异常的情况。
示例如下
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
System.out.println(map.getOrDefault(1, 0)); // 打印 1
System.out.println(map.getOrDefault(2, 0)); // 打印 0
方法接收两个参数putIfAbsent(key, value)
,如果key已存在于map中,则不会覆盖,否则设值。
示例如下
Map<Integer, Integer> map = new HashMap<>();
map.putIfAbsent(1, 1);
map.putIfAbsent(1, 2);
System.out.println(map.get(1));// 打印为 1
该方法有两个重载
replace(key, value)
,替换指定的key的value值,与put不同的是,如果该key不存在,则不进行替换。
replace(key, oldValue, newValue)
,替换指定key的value值,且需要比较老值是否相同,如果不相同,也不进行替换。
示例如下
replace(key, value)
的情况
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
System.out.println(map.replace(1, 2)); // 打印 1 说明返回了老的key
System.out.println(map.replace(2, 2)); // 打印 null
System.out.println(map.get(1)); // 打印 2
replace(key, oldValue, newValue)
的情况
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
System.out.println(map.replace(1, 2, 2)); // 打印 false 说明替换失败
System.out.println(map.replace(1, 1, 2)); // 打印 true 说明替换成功
System.out.println(map.get(1)); // 打印 2
replaceAll(BiFunction)
,接收两个参数的lambda表达式,替换对应的value值。
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 3);
map.put(2, 2);
map.put(3, 1);
map.replaceAll((k, v) -> k * 10);
System.out.println(map);// {1=10, 2=20, 3=30}
map.replaceAll((k, v) -> v * 10);
System.out.println(map);// {1=100, 2=200, 3=300}
我们可以看到,看似是k*10,其实最终改变的还是value值,和k值无关。
compute(key, BiFunction)
,接收两个参数,第一个参数是key,第二个参数是两个参数的lambda表达式,可以执行替换或者新增的作用,返回值计算好的value。
示例如下
// 创建一个包含一些键值对的映射
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
// 使用 compute 方法对指定键进行重新映射
String keyToCompute = "A";
map.compute(keyToCompute, (key, oldValue) -> oldValue * 10);
// 输出更新后的映射
System.out.println(map); // {A=10, B=2}
// 使用 compute 方法添加新的键值对
String newKeyToCompute = "C";
map.compute(newKeyToCompute, (key, oldValue) -> 5);
// 再次输出更新后的映射
System.out.println(map); // {A=10, B=2, C=5}
我们可以看到,第一次起到了值替换的作用,第二次则可以添加不存在的kv值。
computeIfPresent(key, BiFunction)
接收两个参数,第一个参数是key,第二个参数是两个参数的lambda表达式,它针对的是已存在的key值,进行计算,如果不存在某个key,则不会进行什么操作。
示例如下
Map<String, Double> salaryMap = new HashMap<>();
// 添加员工工资信息到映射
salaryMap.put("Alice", 5000.0);
salaryMap.put("Bob", 6000.0);
salaryMap.put("Charlie", 7500.0);
// 使用computeIfPresent给每个员工加薪10%
salaryMap.computeIfPresent("Alice", (name, salary) -> salary * 1.10);
salaryMap.computeIfPresent("Bob", (name, salary) -> salary * 1.10);
salaryMap.computeIfPresent("Charlie", (name, salary) -> salary * 1.10);
// 尝试给不存在的员工加薪,但不会执行操作
salaryMap.computeIfPresent("David", (name, salary) -> salary * 1.10);
// 打印加薪后的工资信息
System.out.println("加薪后的工资信息: " + salaryMap); // 加薪后的工资信息: {Bob=6600.0, Alice=5500.0, Charlie=8250.0}
我们可以看到,David没有被加入到对应的map中。
computeIfAbsent(key, Function)
接收两个参数,第一个参数是key,第二个参数是一个参数的lambda表达式,它针对的是不存在的key值,一般设置默认值并进行计算,当然如果存在了,也会返回对应的v。
示例如下
因为这个我印象深刻,上面也用到了,所以在来一个例子加深一下。
public class ListMapExample {
public static void main(String[] args) {
// 创建一个映射,用于存储城市和其下属的商店列表
Map<String, List<String>> cityStoreMap = new HashMap<>();
// 向映射中添加商店信息
addStore(cityStoreMap, "New York", "Store A");
addStore(cityStoreMap, "New York", "Store B");
addStore(cityStoreMap, "Los Angeles", "Store X");
// 打印城市和商店列表
for (Map.Entry<String, List<String>> entry : cityStoreMap.entrySet()) {
String city = entry.getKey();
List<String> stores = entry.getValue();
System.out.println("城市: " + city);
System.out.println(" 商店列表: " + stores);
}
//城市: New York
// 商店列表: [Store A, Store B]
//城市: Los Angeles
// 商店列表: [Store X]
}
// 使用computeIfAbsent方法添加商店信息到列表
private static void addStore(Map<String, List<String>> cityStoreMap, String city, String store) {
cityStoreMap
.computeIfAbsent(city, k -> new ArrayList<>()) // 如果城市不存在,则创建一个新的列表
.add(store); // 将商店添加到城市的商店列表中
}
}
我们看addStore
方法,它会将map中的城市作为k,商店列表作为v,而如果城市不存在,则创建列表,存在了,因为返回的是k对应的列表,所以正好能够添加到那个列表中去,起到了分组的作用。
merge(key, value,BiFunction)
接收三个参数,第一个参数是key,第二个参数是newValue,第三个参数是两个参数的的lambda表达式,但这里需要注意,使用的是当前k的oldValue和newValue,而不是k,v。
示例如下
public class WordFrequencyCounter {
public static void main(String[] args) {
String text = "This is a sample text It is a simple example";
// 创建一个映射,用于存储单词和它们的出现次数
Map<String, Integer> wordFrequencyMap = new HashMap<>();
// 将文本拆分成单词并统计每个单词的出现次数
String[] words = text.split("\\s+"); // 以空格分割文本
for (String word : words) {
// 使用merge方法更新单词的出现次数
wordFrequencyMap.merge(word, 1, (v1,v2)->v1+v2);
}
System.out.println(wordFrequencyMap);// {a=2, This=1, simple=1, is=2, It=1, text=1, sample=1, example=1}
}
}
分为两种情况讨论。
1.当k不存在,我们就使用newValue来填充。
2.当k存在,则通过oldValue和newValue进行求和,计算放入map。
有时间会再整理下ConcurrentHashMap
,未完待续……
你怎么会没有选择,没有机会呢?时代在一直向前, 这种情况几乎不会发生。