Java将List转换为Map
在日常编码中,我们经常需要从Map中获取List以满足各种需求。在这篇文章中,学习Java中如何将给定的List转换为Map。
1.引言
List接口是Collection的子接口。它是有序的、基于索引的,并允许重复元素。List接口有各种实现类,如ArrayList、LinkedList等。
Map接口表示一组对象,以键值对的形式存在。Map的键始终是唯一的,意味着在Map中不允许重复的键,但可以有重复的值。Map接口有各种实现类,如HashMap、LinkedHashMap、TreeMap等。
2.将List转换为Map的不同方式
现在让我们看看通过哪些方式可以将List转换为Map。
我们有一个包含id和name字段的Employee记录。为了演示,我们创建了两个列表,
- ‘uniqueEmployeeList’包含具有所有员工唯一id的员工数据。
- ‘duplicateEmployeeList’包含一些员工具有重复ID的员工数据。
record Employee(int id, String name) { }
//创建唯一的员工数据列表 List<Employee> uniqueEmployeeList = new ArrayList<>(); // 创建重复的员工数据列表 List<Employee> duplicateEmployeeList = new ArrayList<>(); //将记录添加到两个列表
2.1. 使用forEach()循环
让我们使用forEach()循环遍历员工列表,并使用put()方法将相应的键值添加到Map中。
Map<Integer, Employee> employeeMap = new HashMap<>(); for(Employee employee: uniqueEmployeeList) { employeeMap.put(employee.id(), employee); }
如果我们使用包含重复员工的列表,那么在创建的Map中,旧值将被新值替换为该键。为了避免这种情况,我们可以使用ConcurrentList来存储给定键的所有Employee记录。
Map<Integer, List> employeeMapWithListValue = new HashMap<>(); for(Employee employee: duplicateEmployeeList) { if(employeeMapWithListValue.containsKey(employee.id())) { employeeMapWithListValue.get(employee.id()).add(employee); } else { ArrayList<Employee> list = new ArrayList<>(); list.add(employee); employeeMapWithListValue.put(employee.id(), list); } }
2.2. 使用Collectors.toMap()
自Java版本1.8以来,我们可以使用Streams和collectors通过使用toMap()方法将List转换为Map。
Map<Integer, Employee> employeeMap = uniqueEmployeeList.stream() .collect(Collectors.toMap(Employee::id, Function.identity()));
如果使用包含重复员工的列表,则toMap()方法会引发IllegalStateException。为解决此问题,我们必须使用toMap()方法的另一个变体,该变体将第三个参数作为合并函数,用于解决与同一键关联的值之间的冲突。
Map<Integer, List<Employee>> employeeMapWithListValue = duplicateEmployeeList.stream() .collect(Collectors.toMap(item -> item.id(), item -> new ArrayList<>(Arrays.asList(item)), (l1, l2) -> { l1.addAll(l2); return l1; }));
2.3. 使用Collectors.groupingBy()
当列表中存在重复元素并且我们想要创建一个具有唯一键和与重复键中所有值相关联的所有值的List的Map时,我们可以使用groupingBy()方法。
Map<Integer, List<Employee>> employeeMapWithListValue = duplicateEmployeeList.stream() .collect(Collectors.groupingBy(Employee::id, Collectors.mapping(Function.identity(), Collectors.toList())));
请注意程序输出,与重复的员工ID ‘1’和’2’关联的所有值都在一个List中保留,并返回一个以Integer为键,以List<Employee>为值的Map。
2.4. Apache Commons Collection的MapUtils.populateMap()
我们可以使用Apache Commons Collections库中的MapUtils类来从List创建Map。
Map<Integer, Employee> employeeMap = new HashMap<>(); MapUtils.populateMap(employeeMap, uniqueEmployeeList, Employee::id);
如果列表中存在重复值,我们可以使用Multimap类型,该类型会自动将多个值存储在与单个键映射的列表中。
Multimap employeeMultiMap = ArrayListMultimap.create(); duplicateEmployeeList.stream().forEach(e -> employeeMultiMap.put(e.id(), e));
2.5. 使用Guava的Maps.uniqueIndex()
我们可以使用Guava库的Maps类uniqueIndex()方法来从List创建Map。
Map<Integer, Employee> employeeMap = Maps.uniqueIndex(uniqueEmployeeList, Employee::id);
与上面提到的Collectors.toMap()方法一样,如果列表包含重复记录,则uniqueIndex()方法也会引发IllegalStateException。
为解决此问题,我们可以使用Guava库的MultiMaps.index()方法,该方法执行相同的操作,但返回一个Multimap(可以包含与键相关的任意数量的值),就像Collectors.groupingBy()方法一样。
ImmutableListMultimap<Integer, Employee> employeeMap = Multimaps.index(duplicateEmployeeList, Employee::id);
3.结论
我们已经学习了在Java中将List转换为Map的各种方式。我们涵盖了List包含唯一元素以及List包含重复元素的两种情况。