Jdk8中新增了Lambda表达式和Stream流,而Stream流的操作是在Lambda表达式的基础上进行的。也就是要想流畅的使用Stream流,你得先会点Lambda表达式。
1. Stream流的使用流程:
Stream使用流程需要注意的3点:
- Stream是基于数据源的对象,要先有数据源才能有流,不能凭空
new
一个出来;
- Stream是基于数据源的对象,要先有数据源才能有流,不能凭空
- Stream流的操作不会修改数据源的数据,流的一切操作都是针对生成的流;
- Stream流操作一次后就会失效,就像迭代器一样,想要再次使用必须重新生成一个新的Stream。
2. 获取流
先说一下什么样的数据用Stream处理。生产上用的最多的应该是List<T>集合数据了,可以是int、String的集合,也可以是实体类对象的集合。除此,还有数组和Map都可以用Stream流处理。
- List集合
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
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 中间方法
- filter()
过滤出符合条件的元素
- filter()
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结果
- limit()
截取前面几个
List<String> list2 = list.stream().limit(5).collect(Collectors.toList());
list2.forEach(System.out::println);
limit结果
- skip()
跳过前几个元素
List<String> list3 = list.stream().skip(5).collect(Collectors.toList());
list3.forEach(System.out::println);
skip结果
- distinct()
去重
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结果
- map()
实现流中元素类型的转换。用的比较多有2种情况:
- 获取对象流中某个字段组成集合或计算值
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);
- 由一个对象流,拿到另一个对象流
List<Employe> empList = userList.stream().map(i -> {
Employe emp = new Employe();
emp.setName(i.getName());
emp.setAge(i.getAge());
return emp;
}).collect(Collectors.toList());
- sorted()
排序。这个是比较灵活的,可以直接默认自然排序,也可以重写比较方法,包括多个字段的比较
- 自然排序
// 升序
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升序结果
- 重写排序,一般是对象指定字段排序或多字段排序
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个字段的写法就行。
- flatMap()
处理复杂的数据结构。举个栗子:有一订单列表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 结束方法
- toArray()
意思很明显,就是转为数组。
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));
- count()
计数统计用
long count = list.stream().count();
System.out.println(count);
- max()/min()
找最大/最小的
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结果
- sum()/average()
简单的求和/平均。要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();
- anyMatch()/allMatch()
条件判断。这个感觉就是为了简化代码用的。
比如,看看有没有16岁的学生:
boolean b = userList.stream().anyMatch(i -> i.getAge() == 16);
System.out.println(b);
- collect()
收集。收集成指定类型的数据。
可以灵活的使用自带的api。除了上面那些数量、求和、平均值、最小值、最大值,其他复杂的也可以
- 上面那些已经有的,简单点的
// 数量
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);
- 复杂点的
// 收集成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()
规约。啥是规约,就是把列表流中元素的个数 逐渐减少,最后出的结果是和流中的元素一个类型的。
reduce是很灵活的,所以也是很难的搞的,除非特殊需求,一般用上面的api就能解决。
需要注意一点:你想要通过reduce得到一个什么类型的结果,传给reduce的流中元素必须使用hi这个类型。
reduce 的实现,有3个重新的方法,分别是 1个参数,2个参数,3个参数
- 1个参数的这个
BinaryOperator<P_OUT> accumulator
是规约的核心,最后的结果是根据你这个算法来算的,也是最难得。 - 2个参数的就是多加了个初始值
identity
,当流中没元素的时候,返回这个初始值,相当于1个参数的完善(注意,不为空时,最后的结果是把初始值算进去的,用求和的例子一眼看出)。需要注意的是1个参数和2个参数返回类型不一样。因为1个参数没考虑空的情况。
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);
- 3个参数的
BinaryOperator<R> combiner
是考虑了并行流的情况,是把第2个参数并行的结果用第3个参数处理,一般很少用了,并行就要考虑线程安全问题了。用的时候很容易出错。
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中,别给自己挖坑。
网友评论