Java利用stream将一个List中部分字段复制到另一个List中
例:将MyCrmBaseCustomerInfo的lisi结果复制到UpCustCustomerVO的list中
说明【1、res代指 listParentList 中的每一个数据 2、UpCustCustomerVO类需要有构造参数getCustCode、getName 3、赋值的顺序需要和构造参数顺序一致,CustCode在前,Name在后,顺序乱则赋值乱。】
List<MyCrmBaseCustomerInfo> listParentList = baseMapper.queryPartentInfoTwo(partentList);
List<UpCustCustomerVO> list= listParentList.stream()
.map(res ->new UpCustCustomerVO(res.getCustCode(),res.getName()))
.collect(Collectors.collect);
根据单个条件找出重复数据
List<String> prodCdRepeat = prodVoList.stream().filter(f -> StringUtils.isNotEmpty(f.getProdCdDis()))
.map(ImportProdVo::getProdCdDis).collect(Collectors.groupingBy(Function.identity()))
.values().stream()
.filter(list -> list.size() > 1)
.flatMap(Collection::stream).collect(Collectors.toList());
根据多个条件找出重复数据
List<ImportProdVo> prodVoRepeat = prodVoList.stream().collect(Collectors.groupingBy(p -> p.getProdNm() + p.getSpec()
+ p.getModel() + p.getBrand()))
.values().stream()
.filter(list -> list.size() > 1)
.flatMap(Collection::stream).collect(Collectors.toList());
如果对象不为空
Optional.ofNullable(whVoOne).ifPresent(t -> {
system.out.print("代码体");
});
Optional.ofNullable(whZoneVo).ifPresent(t -> {
item.setUpdPgmId(pgmId);
item.setUpdUserId(userId);
item.setUpdUserNm(userNm);
CommonUtils.setCommonInfo(item, request);
//去除已更新的数据
zoneVos.remove(whZoneVo);
});
**提取id属性,转List **
//提取属性转List<String>
List<String> list = CollectionUtil.getFieldValues(updBoxDetails, "id", String.class);
一、流的初始化与转换:
Java中的Stream的所有操作都是针对流的,所以,使用Stream必须要得到Stream对象:
1、初始化一个流:
Stream stream = Stream.of("a", "b", "c");
2、数组转换为一个流:
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
或者
stream = Arrays.stream(strArray);
3、集合对象转换为一个流(Collections):
List<String> list = Arrays.asList(strArray);
stream = list.stream();
二、流的操作:
流的操作可以归结为几种:
1、遍历操作(map):
使用map操作可以遍历集合中的每个对象,并对其进行操作,map之后,用.collect(Collectors.toList())会得到操作后的集合。
1.1、遍历转换为大写:
List output = wordList.stream().map(String::toUpperCase).collect(Collectors.toList());
1.2、平方数:
List nums = Arrays.asList(1, 2, 3, 4);
List squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
2、过滤操作(filter):
使用filter可以对象Stream中进行过滤,通过测试的元素将会留下来生成一个新的Stream。
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.stream().filter(string -> string.isEmpty()).count();
2.1、得到其中不为空的String
List filterLists = new ArrayList<>();
filterLists.add(“”);
filterLists.add(“a”);
filterLists.add(“b”);
List afterFilterLists = filterLists.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
3、循环操作(forEach):
如果只是想对流中的每个对象进行一些自定义的操作,可以使用forEach:
List forEachLists = new ArrayList<>();
forEachLists.add(“a”);
forEachLists.add(“b”);
forEachLists.add(“c”);
forEachLists.stream().forEach(s-> System.out.println(s));
4、返回特定的结果集合(limit/skip):
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素:
List forEachLists = new ArrayList<>();
forEachLists.add(“a”);
forEachLists.add(“b”);
forEachLists.add(“c”);
forEachLists.add(“d”);
forEachLists.add(“e”);
forEachLists.add(“f”);
List limitLists = forEachLists.stream().skip(2).limit(3).collect(Collectors.toList());
注意skip与limit是有顺序关系的,比如使用skip(2)会跳过集合的前两个,返回的为c、d、e、f,然后调用limit(3)会返回前3个,所以最后返回的c,d,e
5、排序(sort/min/max/distinct):
sort可以对集合中的所有元素进行排序。max,min可以寻找出流中最大或者最小的元素,而distinct可以寻找出不重复的元素:
5.1、对一个集合进行排序:
List sortLists = new ArrayList<>();
sortLists.add(1);
sortLists.add(4);
sortLists.add(6);
sortLists.add(3);
sortLists.add(2);
List afterSortLists = sortLists.stream().sorted((In1,In2)->In1-In2).collect(Collectors.toList());
5.2、得到其中长度最大的元素:
List maxLists = new ArrayList<>();
maxLists.add(“a”);
maxLists.add(“b”);
maxLists.add(“c”);
maxLists.add(“d”);
maxLists.add(“e”);
maxLists.add(“f”);
maxLists.add(“hahaha”);
int maxLength = maxLists.stream().mapToInt(s->s.length()).max().getAsInt();
System.out.println(“字符串长度最长的长度为”+maxLength);
5.2.1、list对象中取某个字符串值的最大值(max)
5.4、根据某个字段进行分组
List<TManufOrdEx> batchNoManufOrdList = tManufOrds.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()-> new
TreeSet<>(Comparator.comparing(TManufOrdEx :: getBatchNo))),ArrayList::new));
5.5、根据某几个字段分组
List<TManufOrdEx> batchNoManufOrdList = tManufOrds.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()-> new
TreeSet<>(Comparator.comparing(e -> e.getDaySort() + ";" + e.getEnterVatOrdDt() + ";" + e.getMachineId() + ";"))),ArrayList::new));
5.6、根据某个字段分组【bigdecimal求和】
List<TManufOrdEx> sumKgsList = tManufOrdExList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(o ->o.getSellConLineNo()))),ArrayList::new));
//筛选过后的订单总数求和
tManufOrdEx.setSumKgs(BigDecimal.valueOf(sumKgsList.stream().mapToDouble(e->e.getKgs().doubleValue()).reduce(0,Double::sum)));
6、匹配(Match方法):
有的时候,我们只需要判断集合中是否全部满足条件,或者判断集合中是否有满足条件的元素,这时候就可以使用match方法:
allMatch:Stream 中全部元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
6.1、判断集合中没有有为‘c’的元素:
List matchList = new ArrayList<>();
matchList.add(“a”);
matchList.add(“a”);
matchList.add(“c”);
matchList.add(“d”);
boolean isExits = matchList.stream().anyMatch(s -> s.equals(“c”));
6.2、判断集合中是否全不为空:
List matchList = new ArrayList<>();
matchList.add(“a”);
matchList.add(“”);
matchList.add(“a”);
matchList.add(“c”);
matchList.add(“d”);
boolean isNotEmpty = matchList.stream().noneMatch(s -> s.isEmpty());
则返回的为false
7.Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
实例1:
List<RmInterview> rmInterviewList = Optional.ofNullable(interviewMapperCustom.selectByExample(rmInterviewExample)).map(
t->t.stream().filter(
each->each.getMainInfoId().equals(maininfoid)
&&each.getInterviewType().equals(finalInterviewType)
&& each.getDeleteFlag().equals(NumberUtils.toByte("0"))
).collect(Collectors.toList())).orElse(Lists.newArrayList());
Stream 完整实例
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
public class Java8Tester {
public static void main(String args[]){
System.out.println("使用 Java 7: ");
// 计算空字符串
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("列表: " +strings);
long count = getCountEmptyStringUsingJava7(strings);
System.out.println("空字符数量为: " + count);
count = getCountLength3UsingJava7(strings);
System.out.println("字符串长度为 3 的数量为: " + count);
// 删除空字符串
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("筛选后的列表: " + filtered);
// 删除空字符串,并使用逗号把它们合并起来
String mergedString = getMergedStringUsingJava7(strings,", ");
System.out.println("合并字符串: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取列表元素平方数
List<Integer> squaresList = getSquares(numbers);
System.out.println("平方数列表: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
System.out.println("列表: " +integers);
System.out.println("列表中最大的数 : " + getMax(integers));
System.out.println("列表中最小的数 : " + getMin(integers));
System.out.println("所有数之和 : " + getSum(integers));
System.out.println("平均数 : " + getAverage(integers));
System.out.println("随机数: ");
// 输出10个随机数
Random random = new Random();
for(int i=0; i < 10; i++){
System.out.println(random.nextInt());
}
System.out.println("使用 Java 8: ");
System.out.println("列表: " +strings);
count = strings.stream().filter(string->string.isEmpty()).count();
System.out.println("空字符串数量为: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("字符串长度为 3 的数量为: " + count);
filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选后的列表: " + filtered);
mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
System.out.println("列表: " +integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
System.out.println("随机数: ");
random.ints().limit(10).sorted().forEach(System.out::println);
// 并行处理
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的数量为: " + count);
}
private static int getCountEmptyStringUsingJava7(List<String> strings){
int count = 0;
for(String string: strings){
if(string.isEmpty()){
count++;
}
}
return count;
}
private static int getCountLength3UsingJava7(List<String> strings){
int count = 0;
for(String string: strings){
if(string.length() == 3){
count++;
}
}
return count;
}
private static List<String> deleteEmptyStringsUsingJava7(List<String> strings){
List<String> filteredList = new ArrayList<String>();
for(String string: strings){
if(!string.isEmpty()){
filteredList.add(string);
}
}
return filteredList;
}
private static String getMergedStringUsingJava7(List<String> strings, String separator){
StringBuilder stringBuilder = new StringBuilder();
for(String string: strings){
if(!string.isEmpty()){
stringBuilder.append(string);
stringBuilder.append(separator);
}
}
String mergedString = stringBuilder.toString();
return mergedString.substring(0, mergedString.length()-2);
}
private static List<Integer> getSquares(List<Integer> numbers){
List<Integer> squaresList = new ArrayList<Integer>();
for(Integer number: numbers){
Integer square = new Integer(number.intValue() * number.intValue());
if(!squaresList.contains(square)){
squaresList.add(square);
}
}
return squaresList;
}
private static int getMax(List<Integer> numbers){
int max = numbers.get(0);
for(int i=1;i < numbers.size();i++){
Integer number = numbers.get(i);
if(number.intValue() > max){
max = number.intValue();
}
}
return max;
}
private static int getMin(List<Integer> numbers){
int min = numbers.get(0);
for(int i=1;i < numbers.size();i++){
Integer number = numbers.get(i);
if(number.intValue() < min){
min = number.intValue();
}
}
return min;
}
private static int getSum(List numbers){
int sum = (int)(numbers.get(0));
for(int i=1;i < numbers.size();i++){
sum += (int)numbers.get(i);
}
return sum;
}
private static int getAverage(List<Integer> numbers){
return getSum(numbers) / numbers.size();
}
}
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
使用 Java 7:
列表: [abc, , bc, efg, abcd, , jkl]
空字符数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
平方数列表: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9
随机数:
-393170844
-963842252
447036679
-1043163142
-881079698
221586850
-1101570113
576190039
-1045184578
1647841045
使用 Java 8:
列表: [abc, , bc, efg, abcd, , jkl]
空字符串数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9.444444444444445
随机数:
-1743813696
-1301974944
-1299484995
-779981186
136544902
555792023
1243315896
1264920849
1472077135
1706423674
空字符串的数量为: 2
java8 【Stream API 筛选与切片 映射 排序 查找与匹配 归约与收集】
筛选与切片
filter(Predicate) 筛选元素,从流中排除不满足Predicate的某些元素
limit(n) 截断流,使其元素不超过给定数量
skip(n) 跳过前面n个元素,若元素不足n个,则返回空流
distinct() 去重,通过流所生成元素的hashCode()与equals()去除重复元素
实体类
public class User {
private String name; // 姓名
private String age; // 年龄
private double salary; // 薪水
private Status status; // 工作状态:FREE 空闲, BUSY 繁忙 INVOCATION 休假
public enum Status {
BUSY, FREE, INVOCATION;
}
public User(){
}
public User(String name, String age, double salary, Status status) {
this.name = name;
this.age = age;
this.salary = salary;
this.status = status;
}
}
使用
List<User> list = Arrays.asList(
new User("张三", 21, 6000, User.Status.BUSY),
new User("李四", 23, 7000, User.Status.FREE),
new User("王五", 25, 6000, User.Status.INVOCATION),
new User("赵六", 21, 10000, User.Status.BUSY),
new User("赵六", 21, 10000, User.Status.BUSY)
);
// filter 筛选出BUSY状态的员工
list.stream()
.filter(u -> User.Status.BUSY.equals(u.getStatus()))
.forEach(System.out::println);
/* 结果:
User{name='张三', age='21', salary=6000.0, status=BUSY}
User{name='赵六', age='21', salary=10000.0, status=BUSY}
User{name='赵六', age='21', salary=10000.0, status=BUSY}
*/
// 筛选出工资等于6000的一个员工
list.stream()
.filter(u -> u.getSalary() == 6000)
.limit(1)
.forEach(System.out::println);
/* 结果:
User{name='张三', age='21', salary=6000.0, status=BUSY}
*/
// 筛选工资等于6000,不是第一个员工的员工
list.stream()
.filter(u -> u.getSalary() == 6000)
.skip(1)
.forEach(System.out::println);
/* 结果:
User{name='王五', age='25', salary=6000.0, status=INVOCATION}
*/
// 员工列表去重
list.stream()
.distinct()
.forEach(System.out::println);
映射
map(Function<T, R>) 提取元素,使用Function应用到每个元素
flatMap(Function<T, Stream>) 提取元素,应用每个元素得到每一个新的Stream,然后将这些Stream合并成新的Stream
public static void test() {
List<String> list = Arrays.asList("abc", "def", "ghi");
// 将每个元素(这里指的每个元素是指"abc", "def"这种),转换成大写
list.stream()
.map(String::toUpperCase)
.forEach(System.out::print);
/* 结果:
ABCDEFGHI
*/
// 提取所有字符
// 步骤:应用每个元素,比如"abc"将其转换成Stream<Charactor>,然后得到三个Stream<Charactor>,最终合并到一个新的Stream<Character>,类似于list.addAll
list.stream()
.flatMap(Test::strToCharator) // ==> .flatMap(s -> strToCharator(s))
.forEach(System.out::print);
/* 结果:
abcdefghi
*/
}
public static Stream<Character> strToCharator(String s){
List<Character> list = new ArrayList<>();
for (char c : s.toCharArray()) {
list.add(c);
}
return list.stream();
}
//区别
Stream<Stream<Character>> characterStream = list.stream()
.map(Test::strToCharator);
characterStream.forEach(s -> s.forEach(System.out::println));
// s = Stream<Character> ,所有需要再迭代遍历一次,共两次
// 变化为 "abc","bcd", "ghi" --> {a, b, c}, {b, c, d}, {g, h , i} --> { {a, b, c}, {b, c, d}, {g, h , i} }
Stream<Character> characterStream2 = list.stream()
.flatMap(Test::strToCharator);
characterStream2.forEach(System.out::println);
// 变化为 "abc","bcd", "ghi" --> {a, b, c }, {b, c, d}, {g, h , i} --> {a,b,c,d,e,f,g,h,i}
类比于List list1, List list2
list1.add(list2) // 将list2作为list1的元素
list1.addAll(list2) // 将list2中所有元素放入到list1中
排序
sorted() 自然排序
sorted(Comparator) 自定义排序
List<String> list = Arrays.asList("bcd", "abe", "afa");
// 自然排序
list.stream()
.sorted()
.forEach(System.out::println);
// 按照string排序
list.stream()
.sorted(String::compareTo)
.forEach(System.out::println);
// 按照第二个元素排序
list.stream()
.sorted((s1, s2) -> Character.valueOf(s1.charAt(1)).compareTo( Character.valueOf(s2.charAt(1))))
.forEach(System.out::println);
// 等于上面,使用工具类 Comparator 更加方便
list.stream()
.sorted(Comparator.comparing(s -> s.charAt(1)))
.forEach(System.out::println);
查找与匹配
allMatch 流中所有元素都匹配成功,返回true
anyMatch 流中匹配一个元素即返回true
noneMatch 流中所有元素都不匹配,返回true,与allMatch相反
findFirst 返回流中匹配的第一个元素
findAny 返回流中匹配到的第一个元素,由于可能是parallelStream并行方式,所以不等于findFirst
count 返回流中元素个数
max 返回流中最大值
min 返回流中最小值
List<User> list = Arrays.asList(
new User("张三", 21, 6000, User.Status.BUSY),
new User("李四", 23, 7000, User.Status.FREE),
new User("王五", 25, 6000, User.Status.INVOCATION),
new User("赵六", 21, 10000, User.Status.BUSY)
);
// 1. 所有员工是否都处于BUSY状态
boolean b = list.stream().allMatch(u -> User.Status.BUSY.equals(u.getStatus())); // ==> false
// 更好的方式: list.stream().map(User::getStatus).allMatch(User.Status.BUSY::equals)
// 2. 至少有一个员工处理BUSY状态
boolean b2 = list.stream().map(User::getStatus).anyMatch(User.Status.BUSY::equals); // ==> true
// 3. 所有员工都不处于BUSY状态
boolean b3 = list.stream().map(User::getStatus).noneMatch(User.Status.BUSY::equals); // ==> false
// 4. 找出处于BUSY状态的第一个员工
Optional<User> firstUser = list.stream().filter(u -> User.Status.BUSY.equals(u.getStatus())).findFirst(); // firstUser.get() 张三
// 5. 找出任意一个处于BUSY状态的员工
Optional<User> anyUser = list.parallelStream().filter(u -> User.Status.BUSY.equals(u.getStatus())).findAny(); // anyUser.get() 赵六
// 6. 处于BUSY状态的员工数量
long count = list.stream().map(User::getStatus).filter(User.Status.BUSY::equals).count(); // ==> 2
// 7. 找到工资最高的员工
Optional<User> maxSalaryUser = list.stream().max(Comparator.comparingDouble(User::getSalary)); // maxSalaryUser.get() 赵六
// 8. 找到年龄最小的员工
Optional<User> minAgeUser = list.stream().min(Comparator.comparingInt(User::getAge));
归约与收集
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的元素反复结合起来,得到一个值
collect 将流转换成其他形式,接收
BinaryOperator extends BiFunction 所以是 T, T, R, 两个参数,一个返回值
reduce
List<User> list = Arrays.asList(
new User("张三", 21, 6000, User.Status.BUSY),
new User("李四", 23, 7000, User.Status.FREE),
new User("王五", 25, 6000, User.Status.INVOCATION),
new User("赵六", 21, 10000, User.Status.BUSY)
);
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 1. 数组所有元素求和
int sum = Arrays.stream(arr).reduce(0, (x, y) -> x + y); // 55
// 说明:这里identity=0,指代第一次运算的x=0,y=数组第一个元素,也就是1,如果identity=2,那么第一次就是x=2, y=1, 那么最终结果=56
// 由于有第一次运算默认值,所以这里一定有返回值,所以设计为返回int,假设没有初始值,也就是下面这种,不确定元素是否足够,返回Optional
OptionalInt optionalInt = Arrays.stream(arr).reduce((x, y) -> x + y); // optionalInt.getAsInt() == 55
// OptionalInt 为 Optional 专门为int的包装类,比Optional<Integer>性能更好,减少装箱拆箱操作
// 2. 计算所有员工工资总和
Optional<Double> sumSalary = list.stream().map(User::getSalary).reduce(Double::sum);
collect
List<User> list = Arrays.asList(
new User("张三", 21, 6000, User.Status.BUSY),
new User("李四", 23, 7000, User.Status.FREE),
new User("王五", 25, 6000, User.Status.INVOCATION),
new User("赵六", 21, 10000, User.Status.BUSY)
);
// ---------------------- 转换为集合 -------------------
// collect(Collector),可以工具类Collectors生成Collector
// 1. 获取员工姓名列表
// 返回一个list
List<String> userNameList = list.stream().map(User::getName).collect(Collectors.toList());
// 返回set
Set<String> userNameSet = list.stream().map(User::getName).collect(Collectors.toSet());
// 返回hashSet
HashSet<String> userNameHashSet = list.stream().map(User::getName).collect(Collectors.toCollection(HashSet::new));
// ---------------------- 计算 ------------------------
// 2. 得到员工数量
// 总数
Long count = list.stream().collect(Collectors.counting()); // == list.stream().count()
// 工资大于5000员工数量
Long salaryConditionCount = list.stream().map(User::getSalary).filter(s -> s > 5000).collect(Collectors.counting()); // == filter().count()
// 3. 所有员工平均工资
Double averagSalary = list.stream().collect(Collectors.averagingDouble(User::getSalary));
// 4. 最高/最低工资
Optional<Double> maxSalary = list.stream().map(User::getSalary).collect(Collectors.maxBy(Double::compare)); // == map().max()
// -------------------- 分组 --------------------------
// 类似于SQL中的group by
// 5. 根据工作状态分组 (单级分组) { BUSY: List<User>, FREE: List<User> }
Map<User.Status, List<User>> userMapByStatus = list.stream().collect(Collectors.groupingBy(User::getStatus));
// 6. 先根据工作状态分组,再根据年龄分组 (多级分组){ BUSY: {21 : List<User>, 23 : List<User>}, FREE : { 25 : List<User>, 26 : List<User> } }
Map<User.Status, Map<Integer, List<User>>> userMapByStatusAndAge = list.stream()
.collect(Collectors.groupingBy(User::getStatus, Collectors.groupingBy(User::getAge)));
// 7. 更多级分组
Map<User.Status, Map<Integer, Map<Double, List<User>>>> userMapByStatusAndAgeAndSalary = list.stream()
.collect(Collectors.groupingBy(User::getStatus, Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getSalary))));
// 8. 自定义分组,先按工作状态分组,再自定义下一次分组key,比如小于30岁,key为青年,大于30岁,key为中年
// ==> { BUSY : { "青年" : List<User>, "老年" : List<User> } }
list.stream().collect(Collectors.groupingBy(User::getStatus, Collectors.groupingBy(u -> {
User user = (User)u;
if (user.getAge() < 30) {
return "青年";
} else {
return "中年";
}
})));
// ------------------ 分区 ------------------------
// 特殊情况下的分组形式,分组条件结果为true/false,即 { true : List<xx>, false : List<xx> }
// 9. 将工资大于6000的分一个区,小于等的分一个区 ==> {true : List<工资大于6000的User>, false : List<工资小于等于6000的User> }
Map<Boolean, List<User>> partitionSalaryMap = list.stream().collect(Collectors.partitioningBy(u -> u.getSalary() > 6000));
// ------------------ 统计 -----------------------
// 返回一个统计结果相关的操作类,可用来得到最大值,最小值等等,当要获取一个集合的各项情况时,这种方式比较好,必须要生成多次流操作获取
DoubleSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingDouble(User::getSalary));
double summaryStatisticsSum = summaryStatistics.getSum(); // 总和,工资总数
long summaryStatisticsCount = summaryStatistics.getCount(); // 总的个数,员工数
double summaryStatisticsMax = summaryStatistics.getMax(); // 最高工资
double summaryStatisticsMin = summaryStatistics.getMin(); // 最低工资
double summaryStatisticsAverage = summaryStatistics.getAverage(); // 平均工资
merge
merge是什么?
merge() 可以这么理解:它将新的值赋值到 key (如果不存在)或更新给定的key 值对应的 value
merge源码参考
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = this.get(key);
V newValue = oldValue == null ? value : remappingFunction.apply(oldValue, value);
if (newValue == null) {
this.remove(key);
} else {
this.put(key, newValue);
}
return newValue;
}
//该方法接收三个参数,一个 key 值,一个 value,
//一个 remappingFunction ,如果给定的key不存在,它就变成了 put(key, value) 。
//但是,如果 key 已经存在一些值,
//我们 remappingFunction 可以选择合并的方式,
//然后将合并得到的 newValue 赋值给原先的 key。
merge() 怎么用?
假设有一个学生成绩对象的列表,对象包含学生姓名、科目、科目分数三个属性,要求求得每个学生的总成绩。加入列表如下:
private List<StudentScore> buildATestList() {
List<StudentScore> studentScoreList = new ArrayList<>();
StudentScore studentScore1 = new StudentScore() {{
setStuName("张三");
setSubject("语文");
setScore(70);
}};
StudentScore studentScore2 = new StudentScore() {{
setStuName("张三");
setSubject("数学");
setScore(80);
}};
StudentScore studentScore3 = new StudentScore() {{
setStuName("张三");
setSubject("英语");
setScore(65);
}};
StudentScore studentScore4 = new StudentScore() {{
setStuName("李四");
setSubject("语文");
setScore(68);
}};
StudentScore studentScore5 = new StudentScore() {{
setStuName("李四");
setSubject("数学");
setScore(70);
}};
StudentScore studentScore6 = new StudentScore() {{
setStuName("李四");
setSubject("英语");
setScore(90);
}};
StudentScore studentScore7 = new StudentScore() {{
setStuName("王五");
setSubject("语文");
setScore(80);
}};
StudentScore studentScore8 = new StudentScore() {{
setStuName("王五");
setSubject("数学");
setScore(85);
}};
StudentScore studentScore9 = new StudentScore() {{
setStuName("王五");
setSubject("英语");
setScore(70);
}};
studentScoreList.add(studentScore1);
studentScoreList.add(studentScore2);
studentScoreList.add(studentScore3);
studentScoreList.add(studentScore4);
studentScoreList.add(studentScore5);
studentScoreList.add(studentScore6);
studentScoreList.add(studentScore7);
studentScoreList.add(studentScore8);
studentScoreList.add(studentScore9);
return studentScoreList;
}
常规做法
ObjectMapper objectMapper = new ObjectMapper();
List<StudentScore> studentScoreList = buildATestList();
Map<String, Integer> studentScoreMap = new HashMap<>();
studentScoreList.forEach(studentScore -> {
if (studentScoreMap.containsKey(studentScore.getStuName())) {
studentScoreMap.put(studentScore.getStuName(),
studentScoreMap.get(studentScore.getStuName()) + studentScore.getScore());
} else {
studentScoreMap.put(studentScore.getStuName(), studentScore.getScore());
}
});
System.out.println(objectMapper.writeValueAsString(studentScoreMap));
结果如下
{"李四":228,"张三":215,"王五":235}
merge() 实现
Map<String, Integer> studentScoreMap2 = new HashMap<>();
studentScoreList.forEach(studentScore -> studentScoreMap2.merge(
studentScore.getStuName(),
studentScore.getScore(),
Integer::sum));
System.out.println(objectMapper.writeValueAsString(studentScoreMap2));
结果如下
{"李四":228,"张三":215,"王五":235}
使用场景
比如分组求和这类的操作,虽然 stream 中有相关 groupingBy() 方法,但如果你想在循环中做一些其他操作的时候,merge() 还是一个挺不错的选择的。