10. JDK8的Stream流

2024-04-21  本文已影响0人  轻轻敲醒沉睡的心灵

Jdk8中新增了Lambda表达式和Stream流,而Stream流的操作是在Lambda表达式的基础上进行的。也就是要想流畅的使用Stream流,你得先会点Lambda表达式。

1. Stream流的使用流程:

Stream使用流程
需要注意的3点:

2. 获取流

先说一下什么样的数据用Stream处理。生产上用的最多的应该是List<T>集合数据了,可以是int、String的集合,也可以是实体类对象的集合。除此,还有数组和Map都可以用Stream流处理。

List<User>  userList = new ArrayList<>();
Long count = userList.stream().collect(Collectors.counting());  
int[] arr = {1,2,3,4,5,6,7,8};
Arrays.stream(arr).forEach(i-> System.out.println(i));
String[] strs = {"a","b","c"};
Arrays.stream(strs).forEach(i-> System.out.println(i));
Map<Integer, List<String>> map = new HashMap<>();
// 1. key的流
map.keySet().stream().forEach(i-> System.out.println(i));
// 2. map的流
map.entrySet().stream().forEach(i-> System.out.println(i));

3 Stream流的常用方法

3.1 中间方法

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("0");
List<String> list1 = list.stream().filter(i -> (Integer.valueOf(i)) % 3 == 0).collect(Collectors.toList());
list1.forEach(System.out::println);
filter结果

截取前面几个

List<String> list2 = list.stream().limit(5).collect(Collectors.toList());
list2.forEach(System.out::println);
limit结果

跳过前几个元素

List<String> list3 = list.stream().skip(5).collect(Collectors.toList());
list3.forEach(System.out::println);
skip结果

去重

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("2");
list.add("3");
list.forEach(System.out::println);
System.out.println("=========");
List<String> list4 = list.stream().distinct().collect(Collectors.toList());
list4.forEach(System.out::println);
distinct结果

实现流中元素类型的转换。用的比较多有2种情况:

  1. 获取对象流中某个字段组成集合或计算值
List<String> nameList = empList.stream().map(employe-> employe.getName()).collect(Collectors.toList());
BigDecimal sum = empList.stream().map(employe-> employe.getSalary()).reduce(BigDecimal.ZERO, BigDecimal::add);
  1. 由一个对象流,拿到另一个对象流
List<Employe> empList = userList.stream().map(i -> {
                        Employe emp = new Employe();
                        emp.setName(i.getName());
                        emp.setAge(i.getAge());
                        return emp;
                    }).collect(Collectors.toList());

排序。这个是比较灵活的,可以直接默认自然排序,也可以重写比较方法,包括多个字段的比较

  1. 自然排序
// 升序
List<String> list5 = list.stream().sorted().collect(Collectors.toList());
list5.forEach(System.out::println);
// 降序
List<String> list6 = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
list6.forEach(System.out::println);
sorted升序结果
  1. 重写排序,一般是对象指定字段排序或多字段排序
List<User> userList = new ArrayList<>();
User u1 = new User(15, "张三1");
User u2 = new User(25, "张三2");
User u3 = new User(16, "李四1");
User u4 = new User(26, "李四2");
User u5 = new User(17, "王五1");
User u6 = new User(27, "王五2");
userList.add(u1);
userList.add(u2);
userList.add(u3);
userList.add(u4);
userList.add(u5);
userList.add(u6);
// 指定字段,默认升序
List<User> list7 = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
// 指定字段降序
List<User> list8 = userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
// 指定字段降序
List<User> list9 = userList.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder())).collect(Collectors.toList());
// 2个字段升序
List<User> list10 = userList.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(Collectors.toList());
// 一降一升1
List<User> list11 = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getName)).collect(Collectors.toList());
// 一降一升2
List<User> list12 = userList.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder()).thenComparing(User::getName)).collect(Collectors.toList());
// 双降,双降也可以第2种写法,这里不写了
List<User> list13 = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getName).reversed()).collect(Collectors.toList());

注意,以上8和9的不同在于:8是用默认的比较器升序排序,然后结果做一个反转变降序;而9直接是用降序比较器做的排序,直接就是降序的结果。
多个字段,就按2个字段的写法就行。

处理复杂的数据结构。举个栗子:有一订单列表List<order> orderList,而每一个订单又包含商品列表List<goods> goodsList,每个商品都有自己的价格price、类别category等属性。要想拿到订单列表中所有商品的流,可以用flatMap()。

// 1. 某个商品一共卖了几件
List<goods> goodsList = orderList.stream().flatMap(order -> order.getGoodsList().stream()).filter(goods -> "12345".equas(goods.getId())).collect(Collectors.toList());

需要注意:flatMap中需要的是一个流对象。

3.2 结束方法

意思很明显,就是转为数组。

Object[] arr = list.stream().toArray();
String[] arr1 = list.stream().toArray(i -> new String[i]);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr1));
toArray结果

其实这个吧,一般也不会用,一般的转 数组用自己的api就行。

String[] arr2 = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(arr2));

计数统计用

long count = list.stream().count();
System.out.println(count);

找最大/最小的

Optional<User> max = userList.stream().max((i1, i2) -> Integer.compare(i1.getAge(), i2.getAge()));
System.out.println(max.get());
Optional<User> min = userList.stream().min((i1, i2) -> i1.getAge() - i2.getAge());
System.out.println(min.get());
max/min结果

简单的求和/平均。要mapTo配合

// 求和
int sum = userList.stream().mapToInt(i -> i.getAge()).sum();
double sum2 = userList.stream().mapToDouble(i -> i.getAge()).sum();
long sum3 = userList.stream().mapToLong(i -> i.getAge()).sum();
// 平均
OptionalDouble sum = userList.stream().mapToInt(i -> i.getAge()).average();
OptionalDouble sum2 = userList.stream().mapToDouble(i -> i.getAge()).average();

条件判断。这个感觉就是为了简化代码用的。
比如,看看有没有16岁的学生:

boolean b = userList.stream().anyMatch(i -> i.getAge() == 16);
System.out.println(b);

收集。收集成指定类型的数据。
可以灵活的使用自带的api。除了上面那些数量、求和、平均值、最小值、最大值,其他复杂的也可以

  1. 上面那些已经有的,简单点的
//  数量
long count = userList.stream().collect(Collectors.counting());
// 求和
Integer si = userList.stream().collect(Collectors.summingInt(i -> i.getAge()));
//  平均
Double ad = userList.stream().collect(Collectors.averagingDouble(i -> i.getAge()));
// 最大
Optional<User> o = userList.stream().collect(Collectors.maxBy((i1, i2) -> i1.getAge() - i2.getAge()));
// 最小
Optional<User> o1 = userList.stream().collect(Collectors.minBy((i1, i2) -> i1.getAge() - i2.getAge()));
// 数量、求和、平均值、最小值、最大值
DoubleSummaryStatistics dss = userList.stream().collect(Collectors.summarizingDouble(i -> i.getAge()));
summarizingDouble结果
这里需要注意一下:对于BigDecimal的处理使用的另外的方法
itemList.stream().map(i -> i.getMoney()).reduce(BigDecimal.ZERO, BigDecimal::add);
  1. 复杂点的
// 收集成list、set、map
List<User> collect = userList.stream().collect(Collectors.toList());
Set<User> set = userList.stream().collect(Collectors.toSet());
Map<String, Integer> map = userList.stream().collect(Collectors.toMap(User::getName, User::getAge));
// 拼接字段成字符串
String nameString = userList.stream().map(i -> i.getName()).collect(Collectors.joining(","));
// 分组,注意分组后的对象格式
Map<Integer, List<User>> m1= userList.stream().collect(Collectors.groupingBy(User::getAge));
// 2个字段分组,注意分组后的对象格式
Map<Integer, Map<String, List<User>>> m2= userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getName)));
// 分组统计,注意分组后的对象格式
Map<Integer, Long> m3 = userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.counting()));

规约。啥是规约,就是把列表流中元素的个数 逐渐减少,最后出的结果是和流中的元素一个类型的。
reduce是很灵活的,所以也是很难的搞的,除非特殊需求,一般用上面的api就能解决。
需要注意一点:你想要通过reduce得到一个什么类型的结果,传给reduce的流中元素必须使用hi这个类型。
reduce 的实现,有3个重新的方法,分别是 1个参数,2个参数,3个参数

reduce的3个实现方法
Optional<Integer> op = userList.stream().map(i -> i.getAge()).reduce((i1, i2) -> i1 + i2);
Integer sum = userList.stream().map(i -> i.getAge()).reduce(2, (i1, i2) -> i1 + i2);
List<Integer> asList = Arrays.asList(1, 2, 3);
Integer reduce = asList.stream().reduce(4, (i1, i2) -> i1 + i2);
System.out.println(reduce);
Integer reduce1 = asList.stream().parallel().reduce(4, (i1, i2) -> i1 + i2);
System.out.println(reduce1);
Integer reduce2 = asList.stream().parallel().reduce(1, (i1, i2) -> i1 + i2, (a, b) -> a * b);
System.out.println(reduce2);
reduce结果

需要注意:并行流别乱用,尤其reduce中,别给自己挖坑。

上一篇 下一篇

猜你喜欢

热点阅读