1. 建立对象类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User
{
private String id;

private String name;

private String sex;

private String city;
}

2. 建立Main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Main
{

public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("3", "王五", "男", "南京"));
userList.add(new User("4", "小明", "男", "广州"));

// key为name,value为user
// user -> user.getName() 也可以写为 User::getName
// Function.identity()也可以写为 user->user
Map<String, User> nameToUserMap = userList.stream().collect(Collectors.toMap(User::getName, Function.identity()));


// key为name,value为city
Map<String, String> nameToCityMap = userList.stream().collect(Collectors.toMap(User::getName, User::getCity));


printMap(nameToUserMap);
printMap(nameToCityMap);
}

private static void printMap(Map<?, ?> map)
{
map.forEach((key, value) -> System.out.println(key + " -> " + value));
}
}

3. 当key有可能重复时,给出合并策略

当key有重复时,Collectors.toMap()方法将抛出java.lang.IllegalStateException: Duplicate key异常,因此需要给出合并策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("3", "王五", "男", "南京"));
userList.add(new User("4", "张三", "男", "广州"));

// 当key重复时,用新值覆盖旧值 (oldValue, newValue) -> newValue,若改为oldValue,则忽略新值保留旧值
Map<String, String> nameToCityMap =
userList.stream().collect(Collectors.toMap(User::getName, User::getCity, (oldValue, newValue) -> newValue));

printMap(nameToCityMap);
}

4. 当stream中可能存在null元素时,先filter过滤

当stream中存在null元素时,Collectors.toMap()方法将抛出NPE异常,因此需要先filter过滤出不为空的元素,再进行转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("3", "王五", "男", "南京"));
userList.add(null);

Map<String, String> nameToCityMap = userList.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(User::getName, User::getCity, (oldValue, newValue) -> newValue));

printMap(nameToCityMap);
}

5. 当Map的value值有可能为null时,先filter过滤

当map的value为null时,Collectors.toMap()方法将抛出NPE异常,因此需要先filter过滤,再进行转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("3", "王五", "男", "南京"));
userList.add(new User("4", "小明", "男", null));

Map<String, String> nameToCityMap = userList.stream()
.filter(Objects::nonNull)
.filter(user -> StringUtils.isNotEmpty(user.getCity()))
.collect(Collectors.toMap(User::getName, User::getCity, (oldValue, newValue) -> newValue));

printMap(nameToCityMap);
}

Tips:

集合类 key value 父类 说明
HashMap 可以为null 可以为null AbstractMap 底层数组+链表实现,线程不安全
ConcurrentHashMap 不能为null 不能为null AbstractMap 线程局部安全
HashTable 不能为null 不能为null Dictionary 底层数组+链表实现,线程安全
TreeMap 不能为null 可以为null AbstractMap 线程不安全

6. 加上排序

将list转化为map时,如果需要排序,只需加上sorted()方法并转换为LinkedHashMap即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("4", "小明", "男", "广州"));
userList.add(new User("3", "王五", "男", "南京"));

Map<String, String> nameToCityMap = userList.stream()
.sorted(Comparator.comparing(User::getId))
.collect(Collectors.toMap(User::getName,
User::getCity, // key为name,value为city
(oldValue, newValue) -> newValue, // key重复时保留新值
LinkedHashMap::new)); // 保留顺序

printMap(nameToCityMap);
}

7. 转换为分组Map

7.1 Collectors.groupingBy

和SQL中的group by语句类似,这里的groupingBy()也是按照某个属性对数据进行分组,属性相同的元素会被对应到Map的同一个key上。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("3", "王五", "男", "南京"));
userList.add(new User("4", "小明", "男", "北京"));

// 按照城市分组,value是个list
Map<String, List<User>> cityToUserMap = userList.stream().collect(Collectors.groupingBy(User::getCity));

printMap(cityToUserMap);
}

7.2 Collectors.partitioningBy

这种情况适用于将Stream中的元素依据某个二值逻辑(满足条件,或不满足)分成互补相交的两部分,比如男女性别、年龄是否达到18岁等。
partitioningBy是一种特殊的groupingBy。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args)
{
List<User> userList = Lists.newArrayList();
userList.add(new User("1", "张三", "男", "北京"));
userList.add(new User("2", "李四", "女", "上海"));
userList.add(new User("3", "王五", "男", "南京"));
userList.add(new User("4", "小明", "男", "北京"));

// key是Boolean,value是个list
Map<Boolean, List<User>> femaleMap =
userList.stream().collect(Collectors.partitioningBy(user -> StringUtils.equals(user.getSex(), "女")));

printMap(femaleMap);
}

Read More

[1]Java Stream API进阶篇
[2]Java 8 – Convert List to Map
[3]How to Convert List to Map in Java
[4]Java 8 – Convert stream to Map
[5]Java 8 Stream - From List to Map