工作中遇到需要将某个List里面的实体类的两个属性对应起来,比如根据姓名找到年龄,就是将List里面的entity属性解析之后放到Map里。
实体类:
public class Person {
private String name;
private Integer age;
...省略getter、setter
}
逻辑:
Person person = new Person("张三",20);
Person person1 = new Person("李四",20);
Person person2 = new Person("张三",20);
List<Person> list = new ArrayList<Person>(){{
add(person);
add(person1);
add(person2);
}};
Map<String, Integer> map = new HashMap<>();
//常规写法
for (Person p : list) {
map.put(p.getName(), p.getAge());
}
System.out.println(map);
//stream 流写法
Map<String, Integer> collect = list.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
System.out.println(collect);
单纯常规写法肯定没什么问题,就是后面的key会被覆盖,但是stream流不行,会报错。
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 20
Collectors.toMap这个方法其实是有三个参数的,第一个是key,第二个是value,第三个是发生冲突的合并规则。
默认采用的就是冲突之后抛出异常的处理。
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
只需要加上第三个参数即可
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
新代码只需要更改第三个参数
即可,会跟常规写法一个效果
Map<String, Integer> collect = list.stream().collect(Collectors.toMap(Person::getName, Person::getAge, (oldValue, newValue) -> newValue));
当然还可以采用Collectors.groupingBy(keyFunction)
的写法,但是返回值会是Map
的形式。这就是新的业务场景了。
附上代码:
Map<String, List<Person>> collect1 = list.stream().collect(Collectors.groupingBy(Person::getName));