赞
踩
篇一中谈到了Lambda的基本语法,核心函数式接口以及方法引用的概念,现在让我们把关注点放到Stream Api上
流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合关注的是数据,而流关注的是计算!
注意:
1. Stream不会自己存储元素。
2. Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
3. Stream操作是延迟的。
Stream的操作大致分成以下三步:
一个数据源(如:集合、数组), 获取一个流
- /**
- * 创建Stream
- */
- @org.junit.Test
- public void test1(){
- //1. 通过Collection系列集合提供的stream()或parallelStream()将集合转换成集合流
- List<String> list = new ArrayList<>();
- Stream<String> stream1 = list.stream();
-
- //2. 通过Arrays的静态方法stream()将数组转换成数组流
- Employee[] employee = new Employee[10];
- Stream<Employee> stream2 = Arrays.stream(employee);
-
- //3. 通过Stream中的静态方法of()
- Stream<String> stream3 = Stream.of("a", "b", "c");
-
- //4. 创建无限流
- //4.1 迭代
- Stream<Integer> stream4 = Stream.iterate(0, x->x+2); //给出一个seed==0,根据一元表达式进行迭代产生无限流
- //stream4.forEach(System.out::println);
-
- //4.2 生成
- Stream<Double> stream5 = Stream.generate(Math::random);
- stream5.forEach(System.out::println);
- }
中间操作链。多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则在执行中间操作时不会执行任何的处理。而在终止操作时一次性全部处理结果的做法被称为"惰性求值"。
筛选与切片:
1. filter(Predicate predicate)
接收Lambda,从流中排除某些元素。
2. distinct()
筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
3. limit(long maxSize)
截断流,使其元素不超过给定的数量。
4. skip(long n)
跳过元素,返回一个扔掉了前n个元素的流。若流中的元素不足n个,则返回一个空流。与limit(long n)互补。
- List<Employee> employees = Arrays.asList(
- new Employee("Jack1", 18, 8888),
- new Employee("Jack2", 20, 6666),
- new Employee("Jack3", 19, 9999),
- new Employee("Jack4", 25, 3333),
- new Employee("Jack4", 25, 3333),
- new Employee("Jack4", 25, 3333)
- );
-
- /**
- * 流水线式的中间操作 --- 筛选与切片
- */
- @org.junit.Test
- public void test(){
- //中间操作 - filter
- Stream<Employee> stream = employees.stream().filter(e -> {
- System.out.println("Stream Api的中间操作");
- return e.getAge() >= 20;
- }).distinct();
-
- //终止操作
- stream.forEach(System.out::println);
-
- //上述测试证实: 单纯的执行中间操作,不会有任何操作被执行。当且仅当执行了终止操作后,中间操作操作会被一次性的全部执行完毕。
- }
-
- /**
- * 流水线式的中间操作 --- 短路
- */
- @org.junit.Test
- public void test2(){
- employees.stream()
- .filter((x) -> x.getSalary() > 3000)
- .limit(2)
- .forEach(System.out::println);
-
- //中间操作会一次性加载所有的条件,如本例中,薪资大于4000的人有3位,但由于limit(2),因此当返回两位员工信息后,stream()就已经短路了。
- //正是有这种短路机制,在某种程度上说,提升了数据迭代的效率。
- }
映射:
1. map(Function f)
接收一个函数作为参数,该函数会被映射到每一个元素上,并将其映射成一个新的元素。
2. mapToDouble(ToDoubleFunction f)
接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的DoubleStream。
3. mapToInt(ToIntFunction f)
接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的IntStream。
4. mapToLong(ToLongFunction f)
接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的LongStream。
5. flatMap(Function f);
接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有的流连接成一个流。
- /**
- * 映射
- */
- @org.junit.Test
- public void test3(){
- List<String> list1 = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
-
- //实际上,这种操作就是在处理流中的每一个元素
- list1.stream()
- .map(String::toUpperCase)
- .forEach(System.out::println);
-
- System.out.println("================================");
-
- //再举例: 从员工列表中提取出员工姓名列表
- employees.stream()
- .map(Employee::getName)
- .forEach(System.out::println);
-
- System.out.println("================================");
-
- //测试flatMap,将list1中的每个字符串拆分成字符,组成新的集合
- list1.stream()
- .flatMap(this::getCharacters) //首先,把方法应用到每一个stream的元素身上,接着,从每一个元素返回的stream中,把子元素抽取出来,组合成一个新的集合。
- .forEach(System.out::print); //输出: aaabbbcccdddeee
-
- System.out.println("================================");
-
- //如果不使用flatMap(),新获得的集合是若干个stream组成的集合
- list1.stream()
- .map(this::getCharacters)
- .forEach(System.out::print); //输出 java.util.stream.ReferencePipeline$Head@71f2a7d5java.util.stream.ReferencePipeline$Head@2cfb4a64j
- }
排序:
1. sorted()
产生一个新流,其中按自然顺序排序。
2. sorted()
产生一个新流,其中按比较器的顺序来排序。
- /**
- * 排序
- * sorted() - 自然排序(Comparable)
- * sorted(Comparator com) - 定制排序(Comparator)
- */
- @org.junit.Test
- public void test4(){
- List<String> list1 = Arrays.asList("ccc", "eee", "aaa", "ddd", "bbb");
-
- //按照自然排序
- list1.stream()
- .sorted()
- .forEach(System.out::print); //输出: aaabbbcccdddeee
- System.out.println();
-
- System.out.println("=====================================");
-
- //按照定制排序
-
- //先按照薪水降序排序,如果薪水相同,再按照姓名降序升序
- employees.stream()
- .sorted((e1, e2)-> {
- if(e1.getSalary() == e2.getSalary()){
- return e1.getName().compareTo(e2.getName());
- }else{
- return -e1.getSalary().compareTo(e2.getSalary());
- }
- })
- .forEach(System.out::println);
- }
一个终止操作,执行中间操作链并产生结果。
1. 查找与匹配
1. allMatch 检查是否匹配所有元素
2. anyMatch 检查是否至少匹配一个元素
3. noneMatch 检查是否没有匹配所有元素
4. findFirst 返回第一个元素
5. findAny 返回当前流中的任意元素
6. count 返回流中元素的总个数
7. max 返回流中的最大值
8. min 返回流中的最小值
- List<Employee> employees = Arrays.asList(
- new Employee("Jack1", 18, 8888D, Status.BUSY),
- new Employee("Jack2", 20, 6666D, Status.RELAX),
- new Employee("Jack3", 19, 9999D, Status.VOCATION),
- new Employee("Jack4", 25, 3333D, Status.RELAX),
- new Employee("Jack4", 25, 3333D, Status.VOCATION),
- new Employee("Jack4", 25, 3333D, Status.BUSY),
- new Employee("Jack5", 25, 7777D, Status.BUSY),
- new Employee("Jack6", 25, 7777D, Status.VOCATION)
- );
-
- @org.junit.Test
- public void test1(){
- boolean b1 = employees.stream()
- .allMatch(e -> e.getStatus().equals(Status.BUSY));
- System.out.println("所有员工是否都处于BUSY状态: " + b1); //false
-
- boolean b2 = employees.stream()
- .anyMatch(e -> e.getStatus().equals(Status.BUSY));
- System.out.println("是否有员工的状态为BUSY: " + b2); //true
-
- boolean b3 = employees.stream()
- .noneMatch(e -> e.getStatus().equals(Status.EXCITING));
- System.out.println("是否没有任何一位员工处于EXCITING状态:" + b3); // true
-
- Optional<Employee> op1 = employees.stream()
- .sorted((e1, e2) -> {
- return -e1.getSalary().compareTo(e2.getSalary());
- })
- .findFirst();
- System.out.println("薪酬待遇最高的员工信息: " + op1.get());
-
- Optional<Employee> op2 = employees.stream()
- .sorted((e1, e2) -> {
- return -e1.getSalary().compareTo(e2.getSalary());
- })
- .findAny();
- System.out.println("任意一名员工的信息: " + op2.get());
-
- Long sum = employees.stream().count();
- System.out.println("员工数量: " + sum);
-
- Optional<Employee> op3 = employees.stream()
- .max((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()));
- System.out.println("薪酬待遇最高的员工信息: " + op3.get());
-
- Optional<Double> op4 = employees.stream()
- .map(Employee::getSalary)
- .min(Double::compare);
- System.out.println("最低工资: " + op4.get());
- }
2. 归约
reduce(T identity, BinaryOperator op) 和 reduce(BinaryOperator) 将流中的元素反复的结合起来,得到一个值。
一般与map结合使用,被称作map-reduce模式。先用map进行数据加工(或提取),再通过reduce对 数据进行反复运算。
- @org.junit.Test
- public void test(){
- List<Integer> lists = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
-
- //求出集合中所有数字的和
- Integer sum = lists.stream()
- .reduce(0, (Integer::sum)); //identity是一个起始值
- System.out.println(sum);
-
- //之所以这里得到的结果时Optional<Integer>,是因为此次归约中没有给定初始值,
- //这样就有可能导致最终的结果为null,因此jdk1.8使用容器类Optional来装载结果
- Optional<Integer> reduce = lists.stream()
- .reduce(Integer::sum);
-
- }
3. 收集 collect
- public void test2(){
- List<String> stringList = employees.stream()
- .map(Employee::getName)
- .collect(Collectors.toList());
- stringList.forEach(System.out::println);
-
- //将收集的结果放入Set中
- Set<String> collect = employees.stream()
- .map(Employee::getName)
- .collect(Collectors.toSet());
-
- //将收集的结果放入任意的Collection集合中
- HashSet<String> collect1 = employees.stream()
- .map(Employee::getName)
- .collect(Collectors.toCollection(HashSet::new)); //Supplier接口,自己实现即可
-
- //总数
- Long sum = employees.stream()
- .collect(Collectors.counting());
-
- //平均值
- Double avgSalary = employees.stream()
- .collect(Collectors.averagingDouble(Employee::getSalary));
-
- //总和
- Double sumSalary = employees.stream()
- .collect(Collectors.summingDouble(Employee::getSalary));
-
- //最大值
- Optional<Employee> op1 = employees.stream()
- .collect(Collectors.maxBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary())));
- }
4. 分组
- /**
- * 分组
- */
- @org.junit.Test
- public void test3(){
- //以状态为员工进行分组
- Map<Status, List<Employee>> collect = employees.stream()
- .collect(Collectors.groupingBy(Employee::getStatus));
-
- System.out.println(collect);
-
- System.out.println("=================================");
-
- //多级分组
- //先按状态分组,再按年龄段分组
- //注意: collect(Function, Collect) 可以无限嵌套
- Map<Status, Map<String, List<Employee>>> map = employees.stream()
- .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
- if(((Employee)(e)).getAge() <= 30){
- return "青年";
- }else if(((Employee)(e)).getAge() <= 50){
- return "中年";
- }else{
- return "老年";
- }
- })));
- System.out.println(map);
- }
5. 分区(分片)
- /**
- * 分区
- * 满足条件和不满足条件,分成“false”和“true”两个区返回
- */
- @org.junit.Test
- public void test4(){
- //按照薪资待遇是否大于7000进行分区
- Map<Boolean, List<Employee>> map = employees.stream()
- .collect(Collectors.partitioningBy((e) -> e.getSalary() > 7000));
- System.out.println(map);
- }
6. 总结(概要)
- /**
- * 总结(概要)
- */
- @org.junit.Test
- public void test5(){
- DoubleSummaryStatistics dss = employees.stream()
- .collect(Collectors.summarizingDouble(Employee::getSalary));
-
- System.out.println(dss.getMax());
- System.out.println(dss.getAverage());
- System.out.println(dss.getCount());
- System.out.println(dss.getMin());
- System.out.println(dss.getSum());
- }
7. 连接
- @org.junit.Test
- public void test6(){
- String joinedStr1 = employees.stream()
- .map(Employee::getName)
- .collect(Collectors.joining(","));
- System.out.println(joinedStr1); // Jack1,Jack2,Jack3,Jack4,Jack4,Jack4,Jack5,Jack6
-
- //如果想在首部或尾部接上特定字符串,可以按照以下写法
- String joinedStr2 = employees.stream()
- .map(Employee::getName)
- .collect(Collectors.joining(",","我爱你","祖国"));
- System.out.println(joinedStr2); //我爱你Jack1,Jack2,Jack3,Jack4,Jack4,Jack4,Jack5,Jack6祖国
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。