Java之流编程 实战:集合与流操作对比 分别使用集合操作及Stream流操作,完成对实际应用场景中的数据处理。直观感受流操作带来的便捷性。
购物车案例 沿用Java之函数编程中的购物车的案例。
需求条件:
想看看购物车中都有什么商品
图书类商品都给买
其余的商品中买两件最贵的
只需要两件商品的名称和总价
传统方式处理业务逻辑 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public static void oldCartHandle () { List<Sku> cartSKuList = CartService.getCartSKuList(); for (Sku sku : cartSKuList) { System.out.println(JSON.toJSONString(sku, true )); } List<Sku> notBooksSkuList = new ArrayList<>(); for (Sku sku : cartSKuList) { if (!SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())) { notBooksSkuList.add(sku); } } notBooksSkuList.sort(new Comparator<Sku>() { @Override public int compare (Sku sku1, Sku sku2) { if (sku1.getTotalPrice() > sku2.getTotalPrice()) { return -1 ; } else if (sku1.getTotalPrice() < sku2.getTotalPrice()) { return 1 ; } else { return 0 ; } } }); List<Sku> topTwoSkuList = new ArrayList<>(); for (int i = 0 ; i < 2 ; i++) { topTwoSkuList.add(notBooksSkuList.get(i)); } Double money = 0.00 ; for (Sku sku : topTwoSkuList) { money += sku.getTotalPrice(); } List<String> resultSkuNameList = new ArrayList<>(); for (Sku sku : topTwoSkuList) { resultSkuNameList.add(sku.getSkuName()); } System.out.println(JSON.toJSONString(resultSkuNameList, true )); System.out.println("商品总价:" + money); }
Lambda、Stream处理业务逻辑 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 public static void newCartHandle () { AtomicReference<Double> money = new AtomicReference<>(0.0 ); List<String> resultSkuNameList = CartService.getCartSKuList() .stream() .peek(sku -> System.out.println(JSON.toJSONString(sku, true ))) .filter(sku -> !SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())) .sorted(Comparator.comparing(Sku::getTotalPrice).reversed()) .limit(2 ) .peek(sku -> money.set(money.get() + sku.getTotalPrice())) .map(Sku::getSkuName) .collect(Collectors.toList()); System.out.println(JSON.toJSONString(resultSkuNameList, true )); System.out.println("商品总价:" + money.get()); }
Stream流 流是什么?
JDK1.8引入的新成员,以声明式方式处理集合数据
将基础操作链接起来,完成复杂的数据处理流水线
提供透明的并行处理
流的简介 从支持数据处理操作 的源 生成的元素序列 。 – Java8实战
元素序列 :在java.util.stream.Stream当中定义了一个新的接口,可以访问特定元素类型的一组有序值。当中有stream方法,可以返回一个流。流的目的在于表达计算
源 :流会使用一个包含数据的源,比如集合,数组,输入输出的资源等。从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
数据处理的操作 :流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中 的常用操作。流可以顺序执行,也可以并行执行。并行执行用parallStream就行。数据处理的操作就是 一个中间操作链,形成一条流的流水线。这些操作会返回一个流,但是如果没有终端操作,这些操作并不会执行。
流与集合的区别
时间(流)与空间(集合)
只能遍历一次
外部迭代与内部迭代
流的组成
流操作分类
流的使用
有状态:有状态就是有数据存储功能,线程不安全
无状态:无状态就是一次操作,不能保存数据。线程安全
实战案例:Stream操作演示 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 package com.java.example.stream;import com.alibaba.fastjson.JSON;import com.java.example.cart.entity.Sku;import com.java.example.cart.enums.SkuCategoryEnum;import com.java.example.cart.service.CartService;import org.testng.annotations.BeforeClass;import org.testng.annotations.Test;import java.util.*;public class StreamOperator { List<Sku> skus; @BeforeClass public void init () { skus = CartService.getCartSKuList(); } @Test public void filterTest () { skus.stream() .filter(sku -> SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void mapTest () { skus.stream() .map(sku -> sku.getSkuName()) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void flatMapTest () { skus.stream() .flatMap(sku -> Arrays.stream(sku.getSkuName().split("" ))) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void peekTest () { skus.stream() .peek(sku -> System.out.println(sku.getSkuName())) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void sortedTest () { skus.stream() .peek(sku -> System.out.println(sku.getSkuName())) .sorted(Comparator.comparing(Sku::getTotalPrice)) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void distinctTest () { skus.stream() .map(Sku::getSkuCategory) .distinct() .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void skipTest () { skus.stream() .sorted(Comparator.comparing(Sku::getTotalPrice)) .skip(3 ) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void limitTest () { skus.stream() .sorted(Comparator.comparing(Sku::getTotalPrice)) .limit(3 ) .forEach(item -> System.out.println(JSON.toJSONString(item, true ))); } @Test public void allMatchTest () { boolean b = skus.stream() .peek(sku -> System.out.println(sku.getSkuName())) .allMatch(sku -> sku.getTotalPrice() > 1000 ); System.out.println(b); } @Test public void anyMatchTest () { boolean b = skus.stream() .peek(sku -> System.out.println(sku.getSkuName())) .anyMatch(sku -> sku.getTotalPrice() > 1000 ); System.out.println(b); } @Test public void noneMatchTest () { boolean b = skus.stream() .peek(sku -> System.out.println(sku.getSkuName())) .noneMatch(sku -> sku.getTotalPrice() > 10000 ); System.out.println(b); } @Test public void findFirstTest () { Optional<Sku> skuOptional = skus.stream().findFirst(); System.out.println(JSON.toJSONString(skuOptional.get(), true )); } @Test public void findAnyTest () { Optional<Sku> skuOptional = skus.stream().findAny(); System.out.println(JSON.toJSONString(skuOptional.get(), true )); } @Test public void maxTest () { OptionalDouble optionalDouble = skus.stream() .mapToDouble(Sku::getTotalPrice) .max(); System.out.println(optionalDouble.getAsDouble()); } @Test public void minTest () { OptionalDouble optionalDouble = skus.stream() .mapToDouble(Sku::getTotalPrice) .min(); System.out.println(optionalDouble.getAsDouble()); } @Test public void countTest () { long count = skus.stream().count(); System.out.println(count); } }
流的构建
由值创建流
由数组创建流
由文件生成流
由函数生成流(无限流)
实战案例:Stream构建四种形式 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.java.example.stream;import org.testng.annotations.Test;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Arrays;import java.util.stream.IntStream;import java.util.stream.Stream;public class StreamConstructor { @Test public void streamFromValue () { Stream<Integer> integerStream = Stream.of(1 , 2 , 3 , 4 , 5 ); integerStream.forEach(System.out::println); } @Test public void streamFromArray () { int [] numbers = {1 , 2 , 3 , 4 , 5 }; IntStream stream = Arrays.stream(numbers); stream.forEach(System.out::println); } @Test public void streamFromFile () throws IOException { Stream<String> lines = Files.lines(Paths.get("/Users/apple/JavaProject/java-sample/java-new/src/main/resources/Interfaces.sql" )); lines.forEach(System.out::println); } @Test public void streamFromFunction () { Stream<Double> generate = Stream.generate(Math::random); generate.limit(100 ).forEach(System.out::println); } }
收集器
将流中的元素累积成一个结果
作用域终端操作collect()上
collect/Collector/Collectors
collect:作为终端操作出现的,流收集的最后一个步骤,一个方法
Collector:是一个接口,collect方法需要接收一个实现了Collector接口的收集器才可以收集
Collectors:是一个工具类,已经提前封装预制了一些实现了Collector接口的收集器,可以直接哪来用
常用预定义收集器功能
将流元素归约和汇总一个值
将流元素分组
将流元素分区
实战案例:演示预定义收集的使用方式 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.java.example.stream;import com.alibaba.fastjson.JSON;import com.java.example.cart.entity.Sku;import com.java.example.cart.service.CartService;import org.testng.annotations.Test;import java.util.List;import java.util.Map;import java.util.stream.Collectors;public class StreamCollector { @Test public void toList () { List<Sku> skus = CartService.getCartSKuList(); List<Sku> collect = skus.stream() .filter(sku -> sku.getTotalPrice() > 100 ) .collect(Collectors.toList()); System.out.println(JSON.toJSONString(collect, true )); } @Test public void group () { List<Sku> skus = CartService.getCartSKuList(); Map<Object, List<Sku>> collect = skus.stream().collect(Collectors.groupingBy(Sku::getSkuCategory)); System.out.println(JSON.toJSONString(collect, true )); } @Test public void partition () { List<Sku> skus = CartService.getCartSKuList(); Map<Boolean, List<Sku>> collect = skus.stream().collect(Collectors.partitioningBy(sku -> sku.getTotalPrice() > 100 )); System.out.println(JSON.toJSONString(collect, true )); } }
归约与汇总
归约(reduce):将Stream流中元素转换成一个值
汇总(collect):将Stream流中元素转换成一个容器
归约 将Stream流中元素转换成一个值 ,只返回一个值
1 2 3 4 5 6 7 8 9 10 Stream<Integer> integerStream = Lists.newArrayList(1 , 2 , 3 ).stream(); integerStream.mapToInt(Integer::intValue).max(); integerStream.mapToInt(Integer::intValue).min(); integerStream.mapToInt(Integer::intValue).sum();
归约操作理解 例如:一个Stream流进行累加
reduce接口参数 以reduce最复杂的接口参数为例
1 2 3 4 5 6 7 8 9 10 11 <U> U reduce (U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) ;
结合图查看接口做了什么
实战案例:自定义归约 根据一批订单信息,计算平均商品价格
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 @Test public void reduceTest () { @Data @AllArgsConstructor class Order { private Integer id; private Integer productCount; private Double totalAmount; } List<Order> list = Lists.newArrayList(); list.add(new Order(1 , 2 , 25.12 )); list.add(new Order(2 , 5 , 257.23 )); list.add(new Order(3 , 3 , 25512.12 )); Order order = list.stream() .parallel() .reduce( new Order(0 , 0 , 0.0 ), (Order order1, Order order2) -> { System.out.println("执行计算逻辑" ); int productCount = order1.getProductCount() + order2.getProductCount(); double totalAmount = order1.getTotalAmount() + order2.getTotalAmount(); return new Order(0 , productCount, totalAmount); }, (Order order1, Order order2) -> { System.out.println("执行合并方法" ); int productCount = order1.getProductCount() + order2.getProductCount(); double totalAmount = order1.getTotalAmount() + order2.getTotalAmount(); return new Order(0 , productCount, totalAmount); } ); System.out.println(JSON.toJSONString(order, true )); }
汇总 将Stream流中元素转换成一个容器
1 2 3 4 5 6 7 8 9 10 11 Stream<Integer> integerStream = Lists.newArrayList(1 , 2 , 3 ).stream(); List<Integer> list = integerStream.collect(Collectors.toList()); Map<Boolean, List<Integer>> partitions = integerStream.collect(Collectors.partitioningBy(item -> item % 2 == 0 )); Map<Boolean, List<Integer>> groups = integerStream.collect(Collectors.groupingBy(item -> item));
collect接口参数 三个参数的接口为例
1 2 3 4 5 6 7 8 9 10 11 <R> R collect (Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) ;
实战案例:自定义收集 根据一批订单信息,计算每个用户的平均商品价格
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 @Test public void collectTest () { @Data @AllArgsConstructor class Order { private Integer id; private String account; private Integer productCount; private Double totalAmount; } List<Order> list = Lists.newArrayList(); list.add(new Order(1 , "zhangsan" , 2 , 25.12 )); list.add(new Order(2 , "zhangsan" , 5 , 257.23 )); list.add(new Order(3 , "lisi" , 3 , 25512.12 )); HashMap<String, Order> orderHashMap = list.stream() .parallel() .collect( () -> { System.out.println("执行初始化容器操作" ); return new HashMap<String, Order>(); }, (HashMap<String, Order> map, Order newOrder) -> { System.out.println("执行新元素添加到容器操作" ); String account = newOrder.getAccount(); if (map.containsKey(account)) { Order order = map.get(account); order.setProductCount(newOrder.getProductCount() + order.getProductCount()); order.setTotalAmount(newOrder.getTotalAmount() + order.getTotalAmount()); } else { map.put(account, newOrder); } }, (HashMap<String, Order> map1, HashMap<String, Order> map2) -> { System.out.println("执行并行结果合并操作" ); map2.forEach((key, value) -> { map1.merge(key, value, (order1, order2) -> { return new Order(0 , key, order1.getProductCount() + order2.getProductCount(), order1.getTotalAmount() + order2.getTotalAmount()); }); }); } ); System.out.println(JSON.toJSONString(orderHashMap, true )); }
问题:多次初始化容器map会不会存在内容泄露风险
解答:方法里面的局部变量,方法执行完,没有外部引用了,自然会被标记为可回收,等待GC回收。
收集器接口 collect接口参数 1 2 3 4 5 6 7 8 <R, A> R collect (Collector<? super T, A, R> collector) ;
收集器接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface Collector <T , A , R > { Supplier<A> supplier () ; BiConsumer<A, T> accumulator () ; BinaryOperator<A> combiner () ; Function<A, R> finisher () ; Set<Characteristics> characteristics () ;
实战案例:查找 实现步骤:
建立数据模型
初始化数据
根据已知条件实现
需求:班级中有多名学生,每名学生有3门课的考试成绩。其中缺考科目分数字段为空。需要找出缺考的学生都叫什么名字。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package com.java.example.stream.cases;import lombok.AllArgsConstructor;import lombok.Data;import org.testng.annotations.BeforeClass;import org.testng.annotations.Test;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class CaseOne { @Data @AllArgsConstructor class ExamStudentScore { private String studentName; private Integer scoreValue; private String subject; } Map<String, List<ExamStudentScore>> stringListMap; @BeforeClass public void init () { stringListMap = new HashMap<>(); List<ExamStudentScore> zsSourceList = new ArrayList<>(); zsSourceList.add(new ExamStudentScore("张三" , 30 , "CHINESE" )); zsSourceList.add(new ExamStudentScore("张三" , 40 , "ENGLISH" )); zsSourceList.add(new ExamStudentScore("张三" , 50 , "MATHS" )); stringListMap.put("张三" , zsSourceList); List<ExamStudentScore> lsSourceList = new ArrayList<>(); lsSourceList.add(new ExamStudentScore("李四" , 80 , "CHINESE" )); lsSourceList.add(new ExamStudentScore("李四" , null , "ENGLISH" )); lsSourceList.add(new ExamStudentScore("李四" , 60 , "MATHS" )); stringListMap.put("李四" , lsSourceList); List<ExamStudentScore> wwSourceList = new ArrayList<>(); wwSourceList.add(new ExamStudentScore("王五" , null , "CHINESE" )); wwSourceList.add(new ExamStudentScore("王五" , null , "ENGLISH" )); wwSourceList.add(new ExamStudentScore("王五" , 70 , "MATHS" )); stringListMap.put("王五" , wwSourceList); } @Test public void findStudent () { stringListMap.forEach((studentName, scoreList) -> { boolean b = scoreList.stream().anyMatch(score -> { System.out.println(score); return score.getScoreValue() == null ; }); if (b) { System.out.println("此学生[ " + studentName + " ]有缺考" ); } }); } }
实战案例:去重 需求:标签管理功能模块。允许用户批量添加标签,后台需要对标签去重,并且需要防止数据库中存在同名的标签。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.java.example.stream.cases;import lombok.AllArgsConstructor;import lombok.Data;import org.testng.annotations.BeforeClass;import org.testng.annotations.Test;import org.testng.collections.Lists;import java.util.List;public class CaseTwo { @Data @AllArgsConstructor class TagReqDTO { private String name; private Integer age; } List<String> tagListFromDB; List<TagReqDTO> tagListFromReq; @BeforeClass public void init () { tagListFromDB = Lists.newArrayList("李四" , "王五" , "赵六" ); tagListFromReq = Lists.newArrayList( new TagReqDTO("张三" , 10 ), new TagReqDTO("李四" , 30 ), new TagReqDTO("王五" , 10 )); } @Test public void distinctTag () { tagListFromReq.stream() .filter(tag -> !tagListFromDB.contains(tag.getName())) .distinct() .forEach(System.out::println); } }
实战案例:扁平化 需求:权限管理功能模块。查询某用户所有角色下所包含的权限名称。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 package com.java.example.stream.cases;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.testng.annotations.BeforeClass;import org.testng.annotations.Test;import org.testng.collections.Lists;import java.util.ArrayList;import java.util.List;public class CaseThree { @Data @AllArgsConstructor @NoArgsConstructor class Role { private List<Permission> permissions; } @Data @AllArgsConstructor class Permission { private String name; } List<Role> roles; @BeforeClass public void init () { roles = new ArrayList<>(); Role adminRole = new Role(); List<Permission> adminPermissionList = Lists.newArrayList( new Permission("删除" ), new Permission("查看" ), new Permission("导出" ) ); adminRole.setPermissions(adminPermissionList); Role userRole = new Role(); List<Permission> userPermissionList = Lists.newArrayList( new Permission("新建" ), new Permission("修改" ), new Permission("删除" ), new Permission("查看" ) ); userRole.setPermissions(userPermissionList); roles.add(adminRole); roles.add(userRole); } @Test public void findPermission () { roles.stream() .flatMap(role -> role.getPermissions().stream()) .peek(permission -> System.out.println("新的流:" + permission)) .distinct() .forEach(System.out::println); } }
实战案例:分组 需求:设计一个对外提供服务的接口,支持调用方传入多个账户编号查询订单。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package com.java.example.stream.cases;import com.alibaba.fastjson.JSON;import lombok.AllArgsConstructor;import lombok.Data;import org.testng.annotations.Test;import org.testng.collections.Lists;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Optional;import java.util.stream.Collectors;import java.util.stream.Stream;public class CaseFour { @Data @AllArgsConstructor class Order { private Integer orderId; private String accountId; } public List<Order> selectFromDB (List<String> accountIds) { List<Order> orders = new ArrayList<>(); for (int i = 0 ; i < 10 ; i++) { orders.add( new Order(i, accountIds.get(i % accountIds.size())) ); } return orders; } public Map<String, List<Order>> queryOrderByAccountIds(List<String> accountIds) { return Optional.ofNullable(selectFromDB(accountIds)) .map(List::stream) .orElseGet(Stream::empty) .collect(Collectors.groupingBy(Order::getAccountId)); } @Test public void test () { Map<String, List<Order>> stringListMap = queryOrderByAccountIds(Lists.newArrayList("张三" , "李四" , "王五" )); System.out.println(JSON.toJSONString(stringListMap, true )); } }
实战案例:排序 需求:在股票中,撮合交易的原则是一段时间内的交易申请,价格越高的先成交;价格一样,下单时间最早的先成交;价格和时间一致,交易量大的先成交;如果价格、时间和交易量都一致,机构优先成交,散户最后成交。
现有一批交易数据,需要确认交易先后顺序。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 package com.java.example.stream.cases;import com.alibaba.fastjson.JSON;import lombok.AllArgsConstructor;import lombok.Data;import org.testng.annotations.BeforeClass;import org.testng.annotations.Test;import java.math.BigDecimal;import java.time.LocalDateTime;import java.util.ArrayList;import java.util.Comparator;import java.util.List;import java.util.stream.Collectors;public class CaseFive { @Data @AllArgsConstructor class Trade { private BigDecimal price; private LocalDateTime time; private Integer count; private String type; } List<Trade> tradeList; @BeforeClass public void init () { tradeList = new ArrayList<>(); tradeList.add(new Trade(new BigDecimal(100 ), LocalDateTime.now().plusSeconds(1 ), 500 , "机构" )); tradeList.add(new Trade(new BigDecimal(101 ), LocalDateTime.now().plusSeconds(2 ), 1 , "个人" )); tradeList.add(new Trade(new BigDecimal(101 ), LocalDateTime.now().plusSeconds(1 ), 1 , "个人" )); tradeList.add(new Trade(new BigDecimal(100 ), LocalDateTime.now().plusSeconds(1 ), 500 , "个人" )); tradeList.add(new Trade(new BigDecimal(100 ), LocalDateTime.now().plusSeconds(0 ), 2 , "个人" )); tradeList.add(new Trade(new BigDecimal(100 ), LocalDateTime.now().plusSeconds(0 ), 100 , "机构" )); } @Test public void sortTrade () { System.out.println(JSON.toJSONString(tradeList, true )); List<Trade> collect = tradeList.stream() .sorted(Comparator .comparing(Trade::getPrice, Comparator.reverseOrder()) .thenComparing(Trade::getTime) .thenComparing(Trade::getCount, Comparator.reverseOrder()) .thenComparing( Trade::getType, (type1, type2) -> { if ("机构" .equals(type1) && "个人" .equals(type2)) { return -1 ; } else if ("个人" .equals(type1) && "机构" .equals(type2)) { return 1 ; } else { return 0 ; } })) .collect(Collectors.toList()); System.out.println("排序后的结果!!!" ); System.out.println(JSON.toJSONString(collect, true )); } }