Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
一、Stream 的三个操作步骤
- 1. 创建Stream
- 2. 中间操作
- 3. 终止操作(终端操作)
1.)创建 Stream 测试代码如下:
package com.xq.stream; import com.xq.design.model.Employee; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class TestStreamAPI1 { //创建 Stream @Test public void test1(){ //1. 可以通过 Collection 系列集合提供的 stream() 或 parallelStream() List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); //2. 通过 Arrays 中的静态方法 stream() 获取数组 Employee[] emps = new Employee[10]; Stream<Employee> streame = Arrays.stream(emps); //3. 通过 Stream 类中的静态方法 of() Stream<String> steam3 = Stream.of("aa","bb","cc"); //4. 创建无线流 // - 迭代 Stream<Integer> stream4 = Stream.iterate(0,(x) -> x + 2); stream4.limit(10).forEach(System.out::println); // - 生成 Stream.generate(() -> Math.random()) .limit(5) .forEach(System.out::println); } }
2)中间操作
- 筛选与切片
- filter —— 接受 Lambda ,从流中抛出某些元素。
- limit --- 截断流,使其元素不超过给定数量。
- skip(n) --- 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
- distinct --- 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
- 映射
- map --- 接收 Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap --- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流。
- 排序
- sorted() ---- 自然排序(Comparable)
- sorted(Comparator com) ---- 定制排序
测试代码如下:
package com.xq.stream; import com.xq.design.model.Employee; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Stream; /** * 中间操作 */ public class TestStreamAPI2 { List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",58,8888.88), new Employee("王五",26,5555.55), new Employee("赵六",36,6666.66), new Employee("田七",12,3333.33), new Employee("赵六",36,6666.66), new Employee("赵六",36,6666.66) ); //内部迭代:迭代操作由 Stream API 完成 @Test public void test1(){ // 中间操作: 不会执行任何操作 Stream stream = employees.stream() .filter((e) -> { System.out.println("Stream API 的中间操作"); return e.getAge() > 35; }); // 终止操作:一次性执行全部内容,即“惰性求值” stream.forEach(System.out::println); } //外部迭代 @Test public void test2(){ Iterator<Employee> it = employees.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } /** * 筛选与切片 * filter —— 接受 Lambda ,从流中抛出某些元素。 * limit --- 截断流,使其元素不超过给定数量。 * skip(n) --- 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 * distinct --- 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 */ @Test public void test3(){ employees.stream() .filter((e) ->{ System.out.println("短路!"); return e.getSalary() > 7000; }) .limit(2) .forEach(System.out::println); } @Test public void test4(){ employees.stream() .filter((e) -> e.getSalary() > 5000) .skip(2) .forEach(System.out::println); } @Test public void test5(){ employees.stream() .filter((e) -> e.getSalary() > 5000) .distinct() .forEach(System.out::println); } /** * 映射 * map --- 接收 Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 * flatMap --- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流。 */ @Test public void test6(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee"); list.stream() .map((str) -> str.toUpperCase()) .forEach(System.out::println); System.out.println("------------------------------"); employees.stream() .map(Employee::getName) .forEach(System.out::println); System.out.println("-----------------------------"); Stream<Stream<Character>> stream = list.stream() .map(TestStreamAPI2::filterCharacter); stream.forEach((sm) -> sm.forEach(System.out::println) ); System.out.println("---------------------------"); Stream<Character> sm = list.stream() .flatMap(TestStreamAPI2::filterCharacter); sm.forEach(System.out::println); } public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for(Character ch :str.toCharArray()){ list.add(ch); } return list.stream(); } /** 排序 * sorted() ---- 自然排序(Comparable) * sorted(Comparator com) ---- 定制排序 */ @Test public void test7(){ List<String> list = Arrays.asList("ccc","aaa","bbb","ddd","eee"); list.stream() .sorted() .forEach(System.out::println); System.out.println("---------------------------------"); employees.stream() .sorted((e1,e2) ->{ if(e1.getAge() == e2.getAge()){ return e1.getName().compareTo(e2.getName()); } else { return -e1.getAge().compareTo(e2.getAge()); } }).forEach(System.out::println); } }
3)终止操作
- 查找与匹配
- allMatch --- 检查是否匹配所有元素
- anyMatch --- 检查是否至少匹配一个元素
- noneMatch --- 检查时候没有匹配所有元素
- findFirst ---- 返回第一个元素
- findAny ---- 返回当前流中的任意元素
- count ---- 返回流中元素的总个数
- max ---- 返回流中最大值
- min ---- 返回流中的最小值
- 规约
- reduce(T identity,BinaryOperator) / reduce(BinaryOperator) ---- 可以将流中元素反复结合起来,得到一个值。
- 收集
- collection ---- 将流转换为其他形式,接受一个 Collection 接口的实现,用于给 Stream 中元素做汇总的方法
测试代码如下:
package com.xq.stream; import com.xq.design.model.Employee; import com.xq.design.model.Employee.Status; import org.junit.jupiter.api.Test; import java.util.*; import java.util.stream.Collectors; /** * 终止操作 */ public class TestStreamAPI3 { List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99, Status.FREE), new Employee("李四",58,8888.88, Status.BUSY), new Employee("王五",26,5555.55, Status.VOCATION), new Employee("赵六",36,6666.66, Status.FREE), new Employee("田七",12,3333.33, Status.BUSY), new Employee("田七",12,3333.33, Status.BUSY) ); /** * 查找与匹配 * allMatch --- 检查是否匹配所有元素 * anyMatch --- 检查是否至少匹配一个元素 * noneMatch --- 检查时候没有匹配所有元素 * findFirst ---- 返回第一个元素 * findAny ---- 返回当前流中的任意元素 * count ---- 返回流中元素的总个数 * max ---- 返回流中最大值 * min ---- 返回流中的最小值 */ @Test public void test1(){ Boolean b1 = employees.stream() .allMatch((e) -> e.getStatus().equals((Status.BUSY))); System.out.println(b1); Boolean b2 = employees.stream() .anyMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b2); Boolean b3 = employees.stream() .noneMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b3); Optional<Employee> op = employees.stream() .sorted((e1,e2) -> -Double.compare(e1.getSalary(),e2.getSalary())) .findFirst(); System.out.println(op.get()); Optional<Employee> op2 = employees.parallelStream() .filter((e) -> e.getStatus().equals(Status.FREE)) .findAny(); System.out.println(op2.get()); } @Test public void test2(){ Long count = employees.stream() .count(); System.out.println(count); Optional<Employee> op1 = employees.stream() .max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())); System.out.println(op1.get()); Optional<Double> op2 = employees.stream() .map(Employee::getSalary) .min(Double::compare); System.out.println(op2.get()); } /** * 规约 * reduce(T identity,BinaryOperator) / reduce(BinaryOperator) ---- 可以将流中元素反复结合起来,得到一个值。 */ @Test public void test3(){ List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sun = list.stream() .reduce(0,(x,y) -> x+y); System.out.println(sun); System.out.println("----------------------"); Optional<Double> op = employees.stream() .map(Employee::getSalary) .reduce(Double::sum); System.out.println(op.get()); } /** * 收集 * collection ---- 将流转换为其他形式,接受一个 Collection 接口的实现,用于给 Stream 中元素做汇总的方法。 */ @Test public void test4(){ List<String> list = employees.stream() .map(Employee::getName) .collect(Collectors.toList()); list.forEach(System.out::println); System.out.println("---------------------"); Set<String> set = employees.stream() .map(Employee::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); System.out.println("----------------------"); HashSet<String> hashSet = employees.stream() .map(Employee::getName) .collect(Collectors.toCollection(HashSet::new)); hashSet.forEach(System.out::println); } @Test public void test5(){ //总数 Long count =employees.stream() .collect(Collectors.counting()); System.out.println(count); //平均值 Double ave = employees.stream() .collect(Collectors.averagingDouble((e) -> e.getSalary())); System.out.println(ave); //总和 Double sum = employees.stream() .collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sum); //最大工资的员工 Optional<Employee> max = employees.stream() .collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()))); System.out.println(max.get()); //最小工资 Optional<Double> min = employees.stream() .map(Employee::getSalary) .collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); } @Test public void test6(){ //按照状态分组 Map<Status,List<Employee>> map = employees.stream() .collect(Collectors.groupingBy(Employee::getStatus)); System.out.println(map); } @Test public void test7(){ //多级分组 Map<Status,Map<String,List<Employee>>> map = employees.stream() .collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy((e) -> { if(((Employee)e).getAge() <= 35){ return "二逼青年"; } else if(((Employee)e).getAge() <= 50){ return "中年"; } else { return "老年"; } }))); System.out.println(map); } @Test public void test8(){ //分区 Map<Boolean,List<Employee>> map = employees.stream() .collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000)); System.out.println(map); } @Test public void test9(){ DoubleSummaryStatistics dss = employees.stream() .collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(dss.getSum()); System.out.println(dss.getAverage()); System.out.println(dss.getMax()); } @Test public void test10(){ String str = employees.stream() .map(Employee::getName) .collect(Collectors.joining(",","==","==")); System.out.println(str); } }