java8流式编程


流式编程

流式编程是对集合的增强,流不是集合的元素,也不是数据存储,更像是迭代器,单向遍历集合中的元素,简化对集合对象的操作(删除、过滤、映射、排序…)

注:流式编程最好的是学习lambda表达式,因为流式操作的参数大多数是函数式接口,使用lambda表达式可以简化操作。

步骤

1.获取数据源,将数据源中的数据读取到流中

2.对流中的数据进行各种各样的处理

3.对流中数据进行整合处理

四大函数式接口

消费型接口

@FunctionalInterface
public interface Consumer<T> {
    //只有参数没有返回值
    void accept(T var1);
}

供给型接口

@FunctionalInterface
public interface Supplier<T> {
    //有返回值没有参数传入
    T get();
}

函数式接口

@FunctionalInterface
public interface ToIntFunction<T> {
    //有参数有一个int类型的返回值
    int applyAsInt(T var1);
}

断定式接口

@FunctionalInterface
public interface Predicate<T> {
    //单一参数,返回值为布尔类型的方法
    boolean test(T var1);
    //...相关default方法删除掉
}

数据源获取

数据源

数据源,流对象中的数据的来源,是集合流式编程的第一步,流所用到的数据源来自集合、数组或者I/O

获取流对象

//1.集合对象
//2.从集合对象获取数据元素
//3.流对象处理数据
//从List集合中获取流对象
    private static void listStream() {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list,0,1,2,3,4,5,6);
        Stream<Integer> stream = list.stream();
        System.out.println(stream);
    }
    //从整数型对象的数据中获取数据元素,创建的流是一个带泛型的流对象Stream<Integer>
    public static void arrayDataSource(){
        Integer[] array = new Integer[]{0,1,2,3,4,5,7,6};
        Stream<Integer> stream = Arrays.stream(array);
        System.out.println(stream);
    }
    //int类型的数组转换成流对象的格式是一个IntStream
    public  static void arrayList(){
        int[] array = new int[]{3,5,6,8,7,9,4};
        IntStream stream = Arrays.stream(array);
        System.out.println(stream);
    }

操作流中的数据

//1.准备数据
    private static Stream<Integer>  getDataSource() {
        //准备一个容器
        List<Integer> list = new ArrayList<>();
        //向数据源添加数据
        Collections.addAll(list,0,2,3,5,4,6,8,7);
        //获取集合的stream对象
        Stream<Integer> stream = list.stream();
        return stream;
    }

#### 最终操作

collect操作 —- 最终操作

将流中的数据收集到一起,对一些数据进行处理,将流中的数据存入一个集合中,collect方法的参数是一个Collector接口,该接口不是一个函数式接口,因此可以实现改接口,可以自定义收集规则,一般不需要,可以使用Collections工具类进行操作。

最终操作: 指的是该操作执行后会将流关闭

collect操作示例: 将流中的数据放入集合中,可以放入List集合、Set集合、Map集合中去。

    //2.collect操作
    public  static  void collection(){
        //获取数据源
        Stream<Integer> dataSource = getDataSource();
        //collect操作
//      List<Integer> collect = dataSource.collect(Collectors.toList());
//        Set<Integer> collect = dataSource.collect(Collectors.toSet());
        Map<String, Integer> collect = dataSource.collect(Collectors.toMap(Object::toString, i -> i));
        System.out.println(collect);
    }
reduce操作——最终操作

reduce的参数是一个BinaryOperator底层实现的是一个函数式的接口,采用lambda操作对集合中的元素求和

 //reduce操作
    public static void reduceUse(){
        //获取流对象
        Stream<Integer> dataSource = getDataSource();
        //2.操作流对象
        Integer reduce = dataSource.reduce(Integer::sum).get();
        System.out.println(reduce);
    }

reduce参数的函数式接口的参数,两个输入参数一个输出参数。

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T var1, U var2);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> var1) {
        Objects.requireNonNull(var1);
        return (var2, var3) -> {
            return var1.apply(this.apply(var2, var3));
        };
    }
}
count操作—统计流中的元素个数
 //count操作
    public static void countUse(){
        //获取数据
        Stream<Integer> dataSource = getDataSource();
        //获取流中的数据
        long count = dataSource.count();
        System.out.println(count);
    }
forEach操作
//foreach操作
public static void foreach(){
     //1.获取数据
     Stream<Integer> dataSource = getDataSource();
     //使用foreach函数
     dataSource.forEach(System.out::println);
}

forEach中的参数是一个消费性接口,只有传入参数没有返回值。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T var1);

    default Consumer<T> andThen(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            this.accept(var2);
            var1.accept(var2);
        };
    }
}
max&&min
 //max&min
    //max  按照比较规则比较流中最大的数据元素
    //min  按照比较规则比较流中最小的数据元素
    public static void maxAndmin(){
        //获取数据源
        Stream<Integer> dataSource = getDataSource();
//        Integer max = dataSource.max(Integer::compareTo).get();
        Integer min = dataSource.min(Integer::compareTo).get();
        System.out.println("数据流中的最大值为:"+" " +"数据流中的最小值为:"+min);
    }
Matching方法

allmatch:只有当流中的所有元素全部匹配时,返回true

anymatch:只有当流中的任意元素匹配时,返回true

nonematch :只有当流中的所有元素全部不匹配时,返回true

 //match方法
    public static void matchAll(){
        Stream<Integer> dataSource = getDataSource();
        //全部匹配,当流中的所有元素全部满足条件时,返回true
//        boolean b = dataSource.allMatch(e -> e >= 0);
//        System.out.println(b);
        //部分匹配
//        boolean b = dataSource.anyMatch(e -> e > 9);
//        System.out.println(b);
        //全部不匹配,返回true
        boolean b = dataSource.noneMatch(e -> e > 10);
        System.out.println(b);
    }

noneMatch传入的参数是一个判定性接口Predicate


@FunctionalInterface
public interface Predicate<T> {
    boolean test(T var1);
}
find操作

findFirst:从流中获取一个元素(获取开头的元素)

findAny:从流中获取一个元素(获取任意一个元素)

大多数情况下两个函数的结果是相同的,多线程操作时结果可能不一致

//find操作
    //findFIrst:获取流中的一个元素,获取的是流中的第一个元素
    //findAny:在单线程操作下获取的元素基本上是流中的首部,并行流中获取的元素可能为首部可能为其他
    public static void findData(){
        //创建集合并添加元素,获取集合流
        List<Integer> dataSource = new ArrayList<>();
        Collections.addAll(dataSource,1,2,3,4,5,6,7,8,9);
        //使用find操作操作流中的数据
//        Integer integer = dataSource.parallelStream().findFirst().get();
//        System.out.println(integer);
        //stream流的findAny操作获取的是流的第一个元素
        //并行流findAny获取的是流中的任一元素
        Integer integer = dataSource.parallelStream().findAny().get();
        System.out.println(integer);
    }

对于以上的部分操作单一的流操作,操作结束后会关闭流,因此使用IntSummaryStatistics获取对象的分析结果,能够将流中的数据操作输出。

public static void testArr(){
        int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
        IntStream stream = Arrays.stream(arr);
        //最终操作,每次操作完都将流关闭,每次执行一行程序
        //获取最大值
//        System.out.println(stream.max().getAsInt());
        //获取最小值
//        System.out.println(stream.min().getAsInt());
        //获取平均值
//        System.out.println(stream.average().getAsDouble());
        //求和操作
//        System.out.println(stream.sum());
        //使用下面的对象可以获取一个流的分析结果,可以将以上所有操作全部记录,不需要每操作一次都关闭流
        IntSummaryStatistics intSummaryStatistics = stream.summaryStatistics();
        System.out.println(intSummaryStatistics.getMin());
        System.out.println(intSummaryStatistics.getMax());
        System.out.println(intSummaryStatistics.getSum());
        System.out.println(intSummaryStatistics.getAverage());
        System.out.println(intSummaryStatistics.getCount());
    }

中间操作

自定义类型进行操作,定义一个学生类

public class Student  implements Comparable<Student>{

    private String name;
    private int age;
    private int score;
    //构造器
    public Student(){}
    public Student(String name,int age,int score){
        this.name = name;
        this.age = age;
        this.score = score;
    }
    //get、set方法
    public void setName(String name){this.name = name;}
    public String getName(){return name;}
    public void setAge(int age){this.age = age;}
    public int getAge(){return age;}
    public void setScore(int score){this.score = score;}
    public int getScore(){return score;}
    //toString方法
    @Override
    public String toString() {
        return "name = "+name+",age = "+age+",score "+score;
    }
    //重写比较方法
    @Override
    public int hashCode() {
        return Objects.hash(name,age,score);
    }

    @Override
    public boolean equals(Object o) {
        if(this == o)return true;
        if(o==null||getClass()!=o.getClass())return false;
        Student stu = (Student) o;
        return age == stu.age && score == stu.score && Objects.equals(name,stu.name);
    }
    //比较规则自定义
    @Override
    public int compareTo(Student o) {
        return score-o.score;
    }
}

测试类中定义一个数据定义的方法

 //创建数据集合并返回数据流对象
    public static Stream<Student> getDataSource(){
        //实例化集合存储Student对象
        ArrayList<Student> students = new ArrayList<>();
        Collections.addAll(students,new Student("01",19,90),
                new Student("02",20,88),
                new Student("03",21,89),
                new Student("04",22,97),
                new Student("05",23,91),
                new Student("06",28,86),
                new Student("05",23,91),
                new Student("06",18,86)
        );
        Stream<Student> stream = students.stream();
        return stream;
    }
过滤函数filter

过滤函数,其参数是一个断定性的接口

 //中间操作,过滤,使用函数过滤不及格的学生
public static void filterDemo(){
        Stream<Student> dataSource = getDataSource();
        dataSource.filter(s->s.getScore()>89).forEach(System.out::println);
 }
去重函数distinct

去掉流中重复的元素,因此自定义的类型需要重写hashCode和equals方法。

 //distinct  可以去掉指定流中的重复数据
    public static void distinct(){
        Stream<Student> dataSource = getDataSource();
        Stream<Student> distinct = dataSource.distinct();
        distinct.forEach(System.out::println);
    }
排序函数sorted

sorted有带参数的和不带参数的两种

sorted():默认排序,指定的类型重写了 Comparable重写比较的方法

sorted(Comparator comp):采用lambda表达式传递参数

/*
* sorted():默认排序可以实现比较接口,自定义排序规则
* sorted(Comparator comp):采用lambda表达式传递参数
* */
    public static void sortedUse(){
        Stream<Student> dataSource = getDataSource();
        //对流数据进行排序
//        dataSource.sorted().forEach(System.out::println);
        //对流中的数据按照自定义的规则进行
        dataSource.sorted((e1,e2)->{return e1.getAge()-e2.getAge();}).forEach(System.out::println);
    }
limit & skip

limit限制,表示截取流中指定数量的数据
skip跳过,表示跳过指定数据,截取剩余部分

 //limit&& skip
    public static void limitandskip(){
        Stream<Student> dataSource = getDataSource();
        dataSource.distinct()
                .sorted((s1,s2)->{return s2.getScore()-s1.getScore();})
                .limit(5)  //分数最高的五个人
                .skip(2)   //跳过2的剩余的输出
                .forEach(System.out::println);
    }
map & mapToInt
//中间操作:map
    //提供一个映射规则,将流中的每一个元素替换成指定的元素
    public static void mapUse(){
        Stream<Student> dataSource = getDataSource();
        //获取所有学生的姓名
//        dataSource.map(s->s.getName()).forEach(System.out::println);
        //获取所有学生的成绩
//        dataSource.map(Student::getScore).forEach(System.out::println);
        IntSummaryStatistics intSummaryStatistics = dataSource.mapToInt(Student::getScore).summaryStatistics();
        System.out.println(intSummaryStatistics.getMax());
    }
flatMap
//中间操作:flatmap
    //扁平化映射:一般在map映射完成后,流中大的数据是一个容器,而我们需要对容器中的数据进行处理
    //使用扁平化映射,可以将流中的容器中的数据直接读取到流中
    public static void flatMap(){
        String[] array = {"hello","world"};
        //将字符串数组中的数据读取到流中
        Stream<String> stream = Arrays.stream(array);
//        stream.map(String::toCharArray).forEach(e-> System.out.println(e));
        stream.map(s->s.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .forEach(System.out::println);
    }

Collections工具类

为了方便获取一个Collector接口的实现类对象,从而使用collect方法,对流中的数据进行处理

方法 描述
Collections.toList() 将流中的数据,整合到一个List集合中
Collections.toSet() 将流中的数据,整合到一个Set集合中
Collections.toMap() 将流中的数据,整合到一个Map集合中
maxBy() 按照指定的规则,找到流中最大的元素,等同于max
minBy() 按照指定的规则,找到流中最小的元素,等同于min
joining() 将流中的数据拼接成一个字符串,(智能操作String的字符串)
summingInt() 将流中的数据,映射成int类型的数据,并求和
averageingInt() 将流中的数据,映射成int类型的数据,并求平均值
summarizingInt() 将流中的数据,映射成int类型的数据,并获取描述信息
 public static void main(String[] args) {
        //准备字符串数组,作为数据源
        String[] dataSource = {"xing","love","the","book"};
        //读取数据源中的数据,做成Stream
        Stream<String> stream = Arrays.stream(dataSource);
        //1.joining只适用于Stream<String>流中
//        String collect = stream.collect(Collectors.joining(" ","{","}"));
//        System.out.println(collect);
        //2.流中的数据映射成int类型并求和
//        Integer collect = stream.collect(Collectors.summingInt(String::length));
//        System.out.println(collect);
        //可以替换上面的两行
        System.out.println(stream.mapToInt(String::length).sum());
    }

文章作者: it星
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 it星 !
 上一篇
jdk自带的分析工具 jdk自带的分析工具
it星
学习环境,jdk1.8,windows7,内存16G。 1.工具学习安装jdk时,在安装目录下的bin文件夹中有很多的相关的工具(**.exe),对于该工具的源码在lib文件中的tools.jar中的sun/tools中。在学习工具前,可以
2020-08-08
下一篇 
java多线程(JUC) java多线程(JUC)
狂神的课程还是蛮好的B站视频 多线程的马士兵也很好,就是不开源,学费有点贵,公开课讲的也挺深入的。 1.什么是JUCjava.util中关于并发的工具包。线程的三种方式:继承Thread类、实现Runnable、实现Callable接口,C
  目录