三种将list转换为map的方法

  在本文中,介绍三种将list转换为map的方法: 

1) 传统方法 

假设有某个类如下 
  
Java代码   收藏代码
  1. class Movie {  
  2.       
  3.     private Integer rank;  
  4.     private String description;  
  5.       
  6.     public Movie(Integer rank, String description) {  
  7.         super();  
  8.         this.rank = rank;  
  9.         this.description = description;  
  10.     }  
  11.       
  12.     public Integer getRank() {  
  13.         return rank;  
  14.     }  
  15.   
  16.     public String getDescription() {  
  17.         return description;  
  18.     }  
  19.   
  20.     @Override  
  21.     public String toString() {  
  22.         return Objects.toStringHelper(this)  
  23.                 .add("rank", rank)  
  24.                 .add("description", description)  
  25.                 .toString();  
  26.     }  
  27. }  


    使用传统的方法: 
   
Java代码   收藏代码
  1. @Test  
  2. public void convert_list_to_map_with_java () {  
  3.       
  4.     List<Movie> movies = new ArrayList<Movie>();  
  5.     movies.add(new Movie(1"The Shawshank Redemption"));  
  6.     movies.add(new Movie(2"The Godfather"));  
  7.   
  8.     Map<Integer, Movie> mappedMovies = new HashMap<Integer, Movie>();  
  9.     for (Movie movie : movies) {  
  10.         mappedMovies.put(movie.getRank(), movie);  
  11.     }  
  12.       
  13.     logger.info(mappedMovies);  
  14.   
  15.     assertTrue(mappedMovies.size() == 2);  
  16.     assertEquals("The Shawshank Redemption", mappedMovies.get(1).getDescription());  
  17. }  


2) JAVA 8直接用流的方法: 
   
Java代码   收藏代码
  1. @Test  
  2. public void convert_list_to_map_with_java8_lambda () {  
  3.       
  4.     List<Movie> movies = new ArrayList<Movie>();  
  5.     movies.add(new Movie(1"The Shawshank Redemption"));  
  6.     movies.add(new Movie(2"The Godfather"));  
  7.   
  8.     Map<Integer, Movie> mappedMovies = movies.stream().collect(  
  9.             Collectors.toMap(Movie::getRank, (p) -> p));  
  10.   
  11.     logger.info(mappedMovies);  
  12.   
  13.     assertTrue(mappedMovies.size() == 2);  
  14.     assertEquals("The Shawshank Redemption", mappedMovies.get(1).getDescription());  
  15. }  



3) 使用guava 工具类库 
   
Java代码   收藏代码
  1. @Test  
  2. public void convert_list_to_map_with_guava () {  
  3.   
  4.      
  5.     List<Movie> movies = Lists.newArrayList();  
  6.     movies.add(new Movie(1"The Shawshank Redemption"));  
  7.     movies.add(new Movie(2"The Godfather"));  
  8.       
  9.      
  10.     Map<Integer,Movie> mappedMovies = Maps.uniqueIndex(movies, new Function <Movie,Integer> () {  
  11.           public Integer apply(Movie from) {  
  12.             return from.getRank();   
  13.     }});  
  14.       
  15.     logger.info(mappedMovies);  
  16.       
  17.     assertTrue(mappedMovies.size() == 2);  
  18.     assertEquals("The Shawshank Redemption", mappedMovies.get(1).getDescription());  
  19. }  
<think> 我们被要求使用Stream API将List转换Map。根据引用[2]和引用[3],我们可以使用`Collectors.toMap`或`Collectors.groupingBy`来实现。 主要两种转换方式: 1. 使用`Collectors.toMap`:将每个元素映射为键值对,适合一对一的映射(即每个键对应一个值)。 2. 使用`Collectors.groupingBy`:将元素按某个属性分组,适合一对多的映射(即每个键对应一个列表的值)。 下面分别详细说明: 方法一:使用`Collectors.toMap`(键唯一) 如果List中的元素转换Map时,键是唯一的,那么可以使用`toMap`。如果键有重复,则会抛出`IllegalStateException`异常,除非我们提供合并函数(merge function)来解决冲突。 基本语法: ```java Map<K, V> map = list.stream() .collect(Collectors.toMap( keyMapper, // 函数:从元素中提取键 valueMapper, // 函数:从元素中提取值 mergeFunction // 可选:当键冲突时,如何合并两个值(例如,如果两个元素有相同的键,则可以选择保留其中一个或合并) )); ``` 示例1:将对象列表转换为以对象某个属性为键,对象本身为值的Map(假设键唯一) ```java List<Person> personList = ...; // 假设Person类有id和name Map<Long, Person> personMap = personList.stream() .collect(Collectors.toMap( Person::getId, // 键:Person对象的id person -> person // 值:对象本身 )); ``` 示例2:如果键可能重复,我们可以指定合并规则(例如,保留先出现的,或后出现的,或合并) ```java // 假设有两个Person对象可能有相同的id,我们想要保留后出现的 Map<Long, Person> personMap = personList.stream() .collect(Collectors.toMap( Person::getId, person -> person, (existing, replacement) -> replacement // 当有重复键时,用新值替换旧值 )); ``` 方法二:使用`Collectors.groupingBy`(键可以重复,一个键对应多个值) 如果我们希望将相同键的元素收集到一个列表中,那么应该使用`groupingBy`。 基本语法: ```java Map<K, List<V>> map = list.stream() .collect(Collectors.groupingBy( keyMapper, // 提取键 Collectors.mapping(valueMapper, Collectors.toList()) // 可选:对每个分组中的元素进行映射并收集为列表 )); ``` 实际上,如果直接使用`groupingBy`只指定键提取函数,那么值默认就是该元素本身,并且收集到一个列表中。 示例3:将相同城市的Person对象分组(键为城市,值为该城市的Person列表) ```java Map<String, List<Person>> peopleByCity = personList.stream() .collect(Collectors.groupingBy(Person::getCity)); ``` 示例4:如果我们想要分组后对元素进行进一步转换(例如,只取名字而不是整个对象) ```java Map<String, List<String>> cityToNames = personList.stream() .collect(Collectors.groupingBy( Person::getCity, Collectors.mapping(Person::getName, Collectors.toList()) )); ``` 引用[2]中展示了使用`groupingBy`将List转换为`Map<String, List<Region>>`,即按父代码分组。 引用[3]中展示了使用`toMap`将List转换为`Map<String, ProjectLineAreaVo>`,其中键由两个属性拼接而成。 注意事项: 1. 使用`toMap`时,如果键重复且没有指定合并函数,会抛出异常。 2. 使用`groupingBy`时,即使键重复也不会抛出异常,而是将相同键的元素收集到列表中。 3. 如果使用`toMap`并且希望值为列表(即一对多),那么不能直接使用`toMap`,而应该使用`groupingBy`。 因此,根据需求选择合适的方法: - 一对一(键唯一):`toMap` - 一对多(键重复,需要分组):`groupingBy` 另外,引用[3]中还有将List中元素的某个属性收集为List的例子: ```java List<Long> idList = list.stream().map(BasSpacePackage::getId).collect(Collectors.toList()); ``` 但这不是转换Map,而是转换List。 总结: 使用Stream API将List转换Map有两种主要方式: 1. 使用`Collectors.toMap`:适合将每个元素转换为唯一的键值对。 2. 使用`Collectors.groupingBy`:适合将元素按键分组,形成键到列表的映射。 根据具体需求选择方法,并注意处理键重复的情况(在`toMap`中必须提供合并函数)。 下面我们给出几个常见的转换示例: 示例5:将List转换Map,其中键是id,值是name(假设id唯一) ```java Map<Long, String> idToNameMap = personList.stream() .collect(Collectors.toMap( Person::getId, Person::getName )); ``` 示例6:将List转换Map,其中键是城市,值是该城市的所有Person的年龄列表 ```java Map<String, List<Integer>> cityToAges = personList.stream() .collect(Collectors.groupingBy( Person::getCity, Collectors.mapping(Person::getAge, Collectors.toList()) )); ``` 示例7:如果键可能重复,且我们想用`toMap`并取第一个值(忽略后面的重复键) ```java Map<Long, Person> firstPersonMap = personList.stream() .collect(Collectors.toMap( Person::getId, person -> person, (first, second) -> first // 遇到重复id时,保留第一个 )); ``` 示例8:使用`groupingBy`并指定下游收集器为计数(统计每个城市的数量) ```java Map<String, Long> countByCity = personList.stream() .collect(Collectors.groupingBy( Person::getCity, Collectors.counting() )); ``` 希望这些示例能帮助您理解如何使用Stream API将List转换Map。 </think> 在 Java Stream API 中将 `List` 转换为 `Map` 主要有两种核心方式,分别使用 `Collectors.toMap()` 和 `Collectors.groupingBy()`。以下是详细方法和示例: --- ### 一、使用 `Collectors.toMap()`(一对一映射) #### 1. **基础转换(键唯一)** ```java List<Person> personList = Arrays.asList( new Person(1, "Alice"), new Person(2, "Bob") ); // 键:id,值:name Map<Integer, String> idToNameMap = personList.stream() .collect(Collectors.toMap( Person::getId, // 键提取函数 Person::getName // 值提取函数 )); // 结果:{1="Alice", 2="Bob"} ``` #### 2. **处理键冲突(重复键合并)** ```java List<Person> duplicateList = Arrays.asList( new Person(1, "Alice"), new Person(1, "Alex") // 相同ID ); // 合并策略:保留后出现的值 Map<Integer, String> mergedMap = duplicateList.stream() .collect(Collectors.toMap( Person::getId, Person::getName, (oldVal, newVal) -> newVal // 冲突处理函数 )); // 结果:{1="Alex"} ``` #### 3. **键为复合值** ```java Map<String, Person> compositeKeyMap = personList.stream() .collect(Collectors.toMap( p -> p.getId() + "_" + p.getName(), // 复合键 p -> p, // 值=对象本身 (oldVal, newVal) -> newVal // 冲突处理 )); // 结果:{"1_Alice": Person@123, "2_Bob": Person@456} ``` --- ### 二、使用 `Collectors.groupingBy()`(一对多分组) #### 1. **基础分组** ```java List<Person> groupList = Arrays.asList( new Person(1, "Alice", "Tech"), new Person(2, "Bob", "Tech"), new Person(3, "Charlie", "HR") ); // 按部门分组 Map<String, List<Person>> deptMap = groupList.stream() .collect(Collectors.groupingBy(Person::getDepartment)); /* 结果: { "Tech": [Person(1,"Alice"), Person(2,"Bob")], "HR": [Person(3,"Charlie")] } */ ``` #### 2. **分组后值转换** ```java // 按部门分组,值提取为名称列表 Map<String, List<String>> deptToNames = groupList.stream() .collect(Collectors.groupingBy( Person::getDepartment, Collectors.mapping(Person::getName, Collectors.toList()) )); // 结果:{"Tech": ["Alice","Bob"], "HR": ["Charlie"]} ``` #### 3. **分组统计** ```java // 按部门统计人数 Map<String, Long> deptCount = groupList.stream() .collect(Collectors.groupingBy( Person::getDepartment, Collectors.counting() )); // 结果:{"Tech":2, "HR":1} ``` --- ### 三、注意事项 1. **键唯一性问题** - 使用 `toMap()` 时需确保键唯一,否则需提供合并函数(如示例2)。 - 使用 `groupingBy()` 时自动处理重复键(值转为集合)。 2. **空指针处理** 若键可能为 `null`,需提前过滤: ```java Map<Integer, String> safeMap = personList.stream() .filter(p -> p.getId() != null) .collect(Collectors.toMap(...)); ``` 3. **自定义Map实现** 指定具体 `Map` 实现类: ```java Map<Integer, Person> linkedMap = personList.stream() .collect(Collectors.toMap( Person::getId, p -> p, (oldVal, newVal) -> newVal, LinkedHashMap::new // 保持插入顺序 )); ``` --- ### 四、方法对比 | 场景 | 推荐方法 | 示例 | |--------------------------|------------------------|---------------------------| | 键值一对一(键唯一) | `Collectors.toMap()` | 用户ID → 用户名 | | 键值一对多(分组) | `Collectors.groupingBy()` | 部门 → 员工列表 | | 键冲突需合并 | `toMap()` + 合并函数 | 相同ID取最新值 | | 分组后聚合操作 | `groupingBy()` + 下游收集器 | 统计部门人数 | > 📌 **最佳实践**: > - 键唯一时优先用 `toMap()`(更简洁)[^2] > - 分组统计用 `groupingBy()` + `mapping()`/`counting()`[^3] > - 复合键用 Lambda 表达式拼接(如 `k1 + "_" + k2`)[^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值