今天看到一道题:
已知一个List<User>集合,User有name(String)和 age(int)属性,请按照年龄从小到大顺序输出每一个年龄的用户(名)列表。
例如:List为[{张三,23},{李四,21},{王五,23}]
输出:21:[李四] , 23:[张三,王五]
首先我想到的就是封装成MAP,把年龄作为key,姓名作为一个List存为value:
public static void getOrderedMap1(List<User> userList){
//对userList排序
Collections.sort(userList);
//封装
Map<Integer,List<String>> userMap = new HashMap<>(userList.size());
//遍历
for (User user:userList){
List<String> username = userMap.get(user.getAge());
//表里没有对应的年龄,就创建新的姓名表
if (username==null){
username = new ArrayList<>();
username.add(user.getName());
//插入到userMap
userMap.put(user.getAge(), username);
}else {
//有年龄就直接加名字
username.add(user.getName());
}
}
userMap.forEach((age,names)->{
System.out.println(
age+":"+names
);
});
}
这个解法好理解,但是手写起来可能有点复杂了,于是利用java8的特性,有了解法2:
public static void getOrderedMap2(List<User> userList) {
Map<Integer, List<String>> userMap = new HashMap<>(userList.size());
//对userList排序
userList.sort(User::compareTo);
userList.forEach(user -> {
if (userMap.get(user.getAge())==null){
List<String> nameList = new ArrayList<>();
nameList.add(user.getName());
userMap.put(user.getAge(),nameList);
}else {
userMap.get(user.getAge()).add(user.getName());
}
});
userMap.forEach((age, names) -> {
System.out.println(
age + ":" + names
);
});
}
同样的方式,先排序,再判断,生成Map丢进去,遍历输出。
我们都既然用了Stream了,干脆就直接用到底吧
public static void getOrderedMap3(List<User> userList) {
userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.toMap(User::getAge, User::getName, (name1, name2) -> name1 + "," + name2))).forEach((age, users) -> {
System.out.println(age + ":[" + users.get(age) + "]");
});
}
这个就更简单了,甚至很粗暴,我们慢慢来讲解。
首先我们把userList作为一个流进行处理。
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。
groupingBy 这个方法是用于生成一个拥有分组功能的Collector,User::getAge表示了我们获得一个根据年龄分组的对象,以及一个Collector对象。
toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map。
我们把遍历的age作为map的key, name作为map的value存入map,重复建处理方案是:当两个name重复,name存入的value应该是“name1,name2”
所以就实现了分类存储。