Java--8--新特性--Stream API

Stream API 提供了一种高效且易于使用的处理数据的方式,(java.util.stream.*)

他可以对数组,集合等做一些操作,最终产生一个新的流,原数据是不会发生改变的。

“集合”讲的是数据,“流”讲的是计算!

注意:

1. Stream自己不会储存元素

2. 不会改变源对象,相反,它会产生一个新的Stream

3. 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行

package StreamP;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Stream1 {
    /**
     * Stream 操作三步骤
     *      1. 创建Stream
     *      2. 中间操作
     *      3. 终止操作
     */
    //创建Stream
    public void test(){
        //可以通过Collection系列集合的 stream()方法 或 parallelStream()创建流
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();

        //可以通过Arrays中的静态方法 stream() 得到一个数组流
        Integer[] integers = new Integer[2];
        Stream<Integer> stream1 = Arrays.stream(integers);

        //通过Stream中的of()方法
        Stream<String> stringStream = Stream.of("a", "b");

        //创建无限流
        //迭代  第一个参数其实就是一个起始值,第二个参数是一个接口,代表一元运算
        Stream<Integer> stream2 = Stream.iterate(0,(x)->x+2);
        //生成  需要的是一个供给型接口
        Stream<Double> generate = Stream.generate(() -> Math.random());
        generate.limit(5).forEach(System.out::println);
//        0.9108574640882595
//        0.8772485678436105
//        0.00318519741701917
//        0.3682731233198696
//        0.39645909717553074
    }
 }

下面是Stream 的中间操作的一些方法

筛选与切片测试代码

package StreamP;

import LambdaP.Employee;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;

public class Stream2 {

    //中间操作:多个中间操作可以连接起来形成一个流水线,除非触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称为惰性求值
    /**
     * 筛选与切片
     *      filter-接收Lambda,从流中排除某些元素
     *      limit-截断流,使元素不超过给定数量
     *      slip(n)-跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补
     *      distinct-筛选,通过流所生成元素的hashCode(),equals()去除重复元素
     */
    public static  List<Employee> employees = Arrays.asList(
            new Employee("张三", 19, 2000),
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 2500),
            new Employee("张七", 42, 5500),
            new Employee("张七", 42, 5500),
            new Employee("张七", 42, 5500)
    );
    public void test(){
        employees.stream().filter((x) -> x.getage()==19).forEach(System.out::println);
        //Employee{name='张三', age=19, salary=2000} 意思就是遍历集合取出每一个对象,如果对象中的Age等于19,那么他就会通过filter
    }
    public void test1(){
        employees.stream().filter((x) ->x.getage()<42).limit(2).forEach(System.out::println);
        //Employee{name='张三', age=19, salary=2000} 意思就是取出age<42的前两个对象 limit跟数据库的limit基本一样
        //Employee{name='张四', age=33, salary=3000}  注意这里的limit,如果找到指定数量的数据后,循环迭代就自己结束了,不会一直遍历
    }
    public void test2(){
        employees.stream().filter((x)->x.getage()<42).skip(2).forEach(System.out::println);
        //LambdaP.Employee{name='张五', age=38, salary=4000}
        //LambdaP.Employee{name='张六', age=41, salary=2500}
        //意思就是跳过前两个,输出满足条件的其他对象数据
    }
    @Test
    public void test3(){
        //到这一步才新添加的重复的“张七”,以上的操作时没有重复元素的
        employees.stream().distinct().forEach(System.out::println);
        //去重操作!注意!!!!!必须重写去重目标对象中的hashCode与equals方法!
    }
}

 映射的操作代码

package StreamP;

import LambdaP.Employee;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Stream3 {
    /**
     * 中间操作‘
     *  映射
     *      map-接收Lambda,将元素转换成其他形式或提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将    其映射成一个新的元素。
     *      flatMap-接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
     */
    public static  List<Employee> employees = Arrays.asList(
            new Employee("张三", 19, 2000),
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 2500),
            new Employee("张七", 42, 5500),
            new Employee("张七", 42, 5500),
            new Employee("张七", 42, 5500)
    );

    public void test(){
        //所有变大写
        List<String> list = Arrays.asList("a","b","c","d","e");
        list.stream()
                .map((x) -> x.toUpperCase())//需要一个Function接口
                .forEach(System.out::println);
        System.out.println("**************************************");
        //取出所有的Name
        employees.stream().map(Employee::getName).distinct().forEach(System.out::println);
        /**
         * map他会将流中的每一个元素都应用到map后的表达式上!
         */
    }

    //需求:把每个字母都转换成char
    public void test1(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        Stream<Character> aChar = getChar(list);
        aChar.forEach(System.out::println);
    }
    public static Stream<Character> getChar(List<String> list){
        List<Character> lists = new ArrayList<>();
        //遍历传入的list,现在的lambda中的e为"aaa","bbb","ccc","ddd","eee"
         list.stream().forEach((e)->{
                char[] ch = e.toCharArray();
                for (char c : ch) {
                    lists.add(c);
                }
        });
        return lists.stream();
    }
    //需求:把每个字母都转换成char
    public void test2(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        //这里的返回是一个流套流,因为map本身会返回一个流,并且调用的方法也返回来一个流
        Stream<Stream<Character>> streamStream = list.stream().map(Stream3::getchar);
        //外层的流
        streamStream.forEach((e) -> {
            //传入的e 为内层流
            e.forEach((r) -> {
                System.out.println(r);
            });
        });
    }
    public static Stream<Character> getchar(String string){
        List<Character> list = new ArrayList<>();
        char[] chars = string.toCharArray();
        for (char aChar : chars) {
            list.add(aChar);
        }
        return list.stream();
    }
    //这时候我们就可以看到map方法对于这种操作的已经有一些麻烦了
    //这时候我们可以考虑使用flatMap,
    @Test
    public void test3(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        list.stream().flatMap(Stream3::getchar).forEach(System.out::println);
        //结果是与上面是一样一样的
    }
    /**
     * map 与 flatMap 的区别
     * map是把每个源数据中的每个元素进行操作,map是一个大的流,那么每个元素就是一个个的小流,map是把每个小流加入到大流中
     * {{a,a,a},{b,b,b},{c,c,c}}
     * flatMap是把源数据中的每个元素中的子元素进行操作,flatMap是一个大流,那么他会把每个子元素加入自己的大流中
     * {a,a,a,b,b,b,c,c,c}
     *
     * map 就类似与List中的add(Object obj)方法,
     * flatMap就类似List中的 addAll(Collection con)方法
     */

}

 排序

package StreamP;

import LambdaP.Employee;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Stream4 {
    /**
     * 排序
     * sorted()-自然排序(Comparable)
     * sorted(Comparator com)-定制排序
     */

    public static List<Employee> employees = Arrays.asList(
            new Employee("张三", 19, 2000),
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 2500),
            new Employee("张六", 41, 1500),
            new Employee("张七", 42, 5500)
    );
    //自然排序
    public void test(){
        List<String> list = Arrays.asList("a","c","r","d","b","z","1");
        list.stream().sorted().forEach(System.out::print);//1abcdrz
    }
    //利用上面集合排序,按名字排序,名字相同按工资排序
    @Test
    public void test1(){
        employees.stream().sorted((x,y)->{
            if(x.getName().equals(y.getName())){
                return Integer.compare(x.getSalary(),y.getSalary());
            }else {
                return x.getName().compareTo(y.getName());
            }
        }).forEach(System.out::println);
//        LambdaP.Employee{name='张七', age=42, salary=5500}
//        LambdaP.Employee{name='张三', age=19, salary=2000}
//        LambdaP.Employee{name='张五', age=38, salary=4000}
//        LambdaP.Employee{name='张六', age=41, salary=1500}
//        LambdaP.Employee{name='张六', age=41, salary=2500}
//        LambdaP.Employee{name='张四', age=33, salary=3000}
    }
}

 查找与匹配

 

package StreamP;

import LambdaP.Employee;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Stream5 {
    public static List<Employee> employees = Arrays.asList(
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 2500),
            new Employee("张三", 19, 2000),
            new Employee("张六", 41, 1500),
            new Employee("张七", 42, 5500)
    );
    /**
     * 查找与匹配
     *      allMatch-检查是否匹配所有元素
     *      anyMatch-检查是否匹配至少一个元素
     *      noneMatch-检查是否没有匹配所有元素
     *      findFirst-返回第一个元素
     *      findAny-返回当前流中的任意元素
     *      count-返回流中总个数
     *      max-返回流中最大值
     *      min-返回流中最小值
     */
    //allMatch测试
    public void test(){
        boolean b = employees.stream().allMatch((e) -> e.getName().getClass()==String .class);
        System.out.println(b);//true  检查名字是不是String类型的,那肯定是啊
        boolean b1 = employees.stream().allMatch((e)->e.getSalary()==1500);
        System.out.println(b1);//false 检查工资是否都等于1500,那肯定不是啊
    }
    //anyMatch测试
    public void test1(){
        boolean zhang = employees.stream().anyMatch((e) -> e.getName().equals("张三"));
        System.out.println(zhang);//检查是否有一个匹配张三的  true
    }
    //noneMatch测试
    public void test2(){
        boolean b = employees.stream().noneMatch((e) -> e.getSalary() == 1500);
        System.out.println(b);//false  检查是否没有匹配所有元素,如果判断条件存在于流中那么他就返回false,如果不存在那么就返回true
    }
    //findFirst测试
    public void test3(){
        //Optional 是java 8 中新增的,为了防止空指针异常,别的帖子会解释这个类
        Optional<Employee> first = employees.stream().findFirst();
        System.out.println(first.get());//LambdaP.Employee{name='张三', age=19, salary=2000}
    }
    //findAny测试
    public void test4(){
        //这里Strem串行流  与 parallelStream并行流 的区别
        //Stream时一条线程去找,并行流即几条线程同时去找,谁找到算谁的
        Optional<Employee> any = employees.parallelStream().findAny();
        System.out.println(any);
    }
    //count测试
    public void test5(){
        long count = employees.stream().count();
        System.out.println(count);//6
    }
    //max测试
    public void test6(){
        //max与min都必须有比较条件才可以
        Optional<Employee> max = employees.stream().max((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());//LambdaP.Employee{name='张七', age=42, salary=5500}
    }
    //min测试
    @Test
    public void test7(){
        Optional<Employee> min = employees.stream().min((e1, e2) -> Integer.compare(e1.getage(), e2.getage()));
        System.out.println(min.get());//LambdaP.Employee{name='张三', age=19, salary=2000}
    }
}

 归约

 

package StreamP;

import LambdaP.Employee;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Stream6 {
    /**
     * 归约
     *   reduce(T identity,BinaryOperator)   /     reduce(BinaryOperator)--可以将流中的元素反腐结合起来,得到一个值。
     */
    public static List<Employee> employees = Arrays.asList(
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 2500),
            new Employee("张三", 19, 2000),
            new Employee("张六", 41, 1500),
            new Employee("张七", 42, 5500)
    );
    public void test(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        //求和
        Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduce);//55
        /**
         * reduce第一个参数是一个起始值,第二个参数是一个二元运算,
         *      这个方法有重载,像上面这个方法的话,
         *      他每次把值都放到y,第一次x起始值为0,所以就是0+y,0+y的结果再次放到x,然后把下一个元素放到y,在进行运算,以此类推
         */
    }
    public void test1(){
        //获取工资总和、
        //获取employees的流,然后取出每个对象的工资,然后求和
        Integer reduce = employees.stream().map(Employee::getSalary).reduce(0, (x, y) -> x + y);
        System.out.println(reduce);//18500
        //***********************************************************************
        //sum 定义如下       public static int sum(int a, int b) {
        //                      return a + b;
        //                 }
        Optional<Integer> reduce1 = employees.stream().map(Employee::getSalary).reduce(Integer::sum);
        System.out.println(reduce1.get());//18500这里为啥是返回Optional?因为上一个重载的reduce有起始值,再怎么滴都是有值的,那么这个是没有起始值保证的
    }
}

 

收集:!!!!!!!
package StreamP;

import LambdaP.Employee;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

public class Stream7 {
    /**
     * 收集:
     *  collect--将流转换为其他形式,接收一个Collector(收集器)接口的实现,用于给Stream中元素做汇总的方法
     *  在这里,java为我们提供了Collector接口的工具类 Collectors实现类,里面有很多静态方法可供调用
     */
    public static List<Employee> employees = Arrays.asList(
            new Employee("张四", 33, 3000),
            new Employee("张五", 38, 4000),
            new Employee("张六", 41, 3000),
            new Employee("张三", 19, 2000),
            new Employee("张7", 41, 3000),
            new Employee("张七", 42, 5500)
    );
    //收集到指定集合中!
    public void test(){
        //需求 ,将名字提取出来,放到List中咋办呢
        List<Employee> collect = employees.stream().collect(Collectors.toList());
        collect.stream().forEach(System.out::println);//即可输出
        System.out.println("***************");
        //如果平时我们需要的收集到的集合没有提供相应的办法咋办
        HashSet<Employee> collect1 = employees.stream().collect(Collectors.toCollection(HashSet::new));
        for (Employee employee : collect1) {
            System.out.println(employee);
        }
    }
    //可以计算各项数据
    public void test1(){
        //求总数
        Long collect = employees.stream().collect(Collectors.counting());
        System.out.println(collect);//6
        //求最小
        Optional<Employee> collect1 = employees.stream().collect(Collectors.minBy((x, y) -> Integer.compare(x.getSalary(), y.getSalary())));
        System.out.println(collect1.get());//LambdaP.Employee{name='张六', age=41, salary=1500}

        System.out.println("*************************");
        //取出工资求工资最小
        Optional<Integer> collect2 = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Integer::compare));
        System.out.println(collect2.get());//1500
        //求最大
        Optional<Employee> collect3 = employees.stream().collect(Collectors.maxBy((x, y) -> x.getage() - y.getage()));
        System.out.println(collect3);//Optional[LambdaP.Employee{name='张七', age=42, salary=5500}]以为没有get,所以外层抱着一个Optional对象
        //求平均
        Double collect4 = employees.stream().collect(Collectors.averagingInt(Employee::getSalary));
        System.out.println(collect4);//3083.3333333333335  这里平均数有三个不同的方法分别是转int,转double,转long,并且后面时自己循环,不需要自己map了
        //求和  同样是三个不同方法 转int,转double,转long
        Double collect6 = employees.stream().collect(Collectors.summingDouble(Employee::getage));
        System.out.println(collect6);//214.0
        // 同样是三个不同方法 转int,转double,转long,但是这里是按照 指定列 把上面的所有参数都求出来的
        DoubleSummaryStatistics collect5 = employees.stream().collect(Collectors.summarizingDouble(Employee::getage));
        System.out.println(collect5);//DoubleSummaryStatistics{count=6, sum=214.000000, min=19.000000, average=35.666667, max=42.000000}
        System.out.println(collect5.getCount());//6
        System.out.println(collect5.getMax());//42。0
    }
    //分组!!
    public void test2(){
        //在这我才更改了上面集合的工资数据,
        Map<Integer, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getSalary));
        System.out.println(collect);//下面的数据就是分组后的,Map的key为分组条件,value为List的分组后的对象
        //{
        //  2000=[LambdaP.Employee{name='张三', age=19, salary=2000}, LambdaP.Employee{name='张六', age=41, salary=2000}],
        //  4000=[LambdaP.Employee{name='张五', age=38, salary=4000}],
        //  3000=[LambdaP.Employee{name='张四', age=33, salary=3000}, LambdaP.Employee{name='张六', age=41, salary=3000}],
        //  5500=[LambdaP.Employee{name='张七', age=42, salary=5500}]
        // }

        ConcurrentMap<Integer, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingByConcurrent(Employee::getSalary));
        System.out.println(collect1);//还没搞清。。。。
        //{
        // 4000=[LambdaP.Employee{name='张五', age=38, salary=4000}],
        // 2000=[LambdaP.Employee{name='张三', age=19, salary=2000}, LambdaP.Employee{name='张六', age=41, salary=2000}],
        // 3000=[LambdaP.Employee{name='张四', age=33, salary=3000}, LambdaP.Employee{name='张六', age=41, salary=3000}],
        // 5500=[LambdaP.Employee{name='张七', age=42, salary=5500}]
        //}
    }
    //多级分组!!
    public void test3(){
        //按工资分,工资一样按年龄分
        Map<Integer, Map<String, List<Employee>>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getSalary, Collectors.groupingBy((e) -> {
            if (e.getage() < 20) {
                return "青年";
            } else if (e.getage() < 40) {
                return "中年";
            } else {
                return "老年";
            }
        })));
        System.out.println(collect);
        //{
        // 2000={青年=[LambdaP.Employee{name='张三', age=19, salary=2000}]},
        // 4000={中年=[LambdaP.Employee{name='张五', age=38, salary=4000}]},
        // 3000={老年=[LambdaP.Employee{name='张六', age=41, salary=3000}, LambdaP.Employee{name='张7', age=41, salary=3000}], 中年=[LambdaP.Employee{name='张四', age=33, salary=3000}]},
        // 5500={老年=[LambdaP.Employee{name='张七', age=42, salary=5500}]}
        //}
    }
    //分区 分为true与false
    public void test4(){
        Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy((e) -> e.getage() > 35));
        System.out.println(collect);
        //{
        // false=[LambdaP.Employee{name='张四', age=33, salary=3000}, LambdaP.Employee{name='张三', age=19, salary=2000}],
        // true=[LambdaP.Employee{name='张五', age=38, salary=4000}, LambdaP.Employee{name='张六', age=41, salary=3000},
        //          LambdaP.Employee{name='张7', age=41, salary=3000}, LambdaP.Employee{name='张七', age=42, salary=5500}]
        //}
    }
    //连接!
    @Test
    public void test5(){
        //参数:第一个是用什么隔开这些需要连接的字符串,第二个是字符串开头,第三个是字符串结尾
        String collect = employees.stream().map(Employee::getName).collect(Collectors.joining(",", "--", "--"));
        System.out.println("collect = " + collect);//collect = --张四,张五,张六,张三,张7,张七--
    }
}

Stream API就到此结束了!

 

posted @ 2017-11-28 11:48  Bap  阅读(527)  评论(0编辑  收藏  举报