简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)

简单学习jdk1.8 StreamAPI(二)

  • Stream介绍
    • 什么是Stream
    • stream操作的三个步骤
      • 新老实现的不同
      • 剖析stream通用语法
      • 使用Stream的基本步骤
      • 详细解释Stream三个步骤
    • 创建Stream的几种方式
      • 1.通过Stream接口的静态工厂方法
        • 1.1 of 通过具体值来创建流
        • 1.2 iterate() generate() 无限流 函数创建无限流
          • 1.2.1 generate() 生成一个无限长度的Stream
          • 1.2.2 iterate()
      • 2.通过Collection接口的默认方法(默认方法:Default method,是Java8中的一个新特性)–stream(),把一个Collection对象转换成Stream ,由Collection子类(List,Set)来创建流
      • 3.有数组创建流
      • 以下是详细的测试案例
    • Stream的中间操作(转换Stream):
      • Stream接口中定义了几个常用的转换方法:
        • 中间操作:筛选与切片(filter,limit,skip(n),distinct)
          • 1.filter
          • 2.distinct
          • 3.distinctByKey
          • 4. limit
          • 5. skip
        • 中间操作:映射
            • 1.map mapToInt mapToLong mapToDouble
          • 2.flatMap flatMapToInt flatMapToLong flatMapToDouble
        • 中间操作:排序
          • sorted ()
          • sorted(Comparator comp)
        • 中间操作消费函数 peek(消费函数)
          • peek()
      • 性能问题
    • 终止操作
      • 遍历
      • 查找与匹配
      • 终止操作:汇聚(Reduce)&收集(Collect)Stream
  • 其他
  • 综合整理
    • 常见中间操作:
    • 常见终端操作 :
    • Optional
      • empty
      • of
      • ofNullable
      • 常用方法:

Stream介绍

什么是Stream

stream是java8中处理数组,集合的抽象概念,他可以指定你希望对集合进行的操作,可以执行非常复杂的 查找 过滤 和 映射数据等操作。
使用Stream API 对集合数据进行操作,就类似于使用sql执行的数据库查询。(给出命令就可以)
一个Stream表面上与一个集合很类似,集合中保存的是数据,而流中设置的是对数据的操作。
Stream的特点:

1.stream自己不会存储元素。
2.stream不会改变源数据,相反,他们会返回一个持有结构的新的stream。
3.stream操作是延时执行的,这意味他们等到需要结果的时候才执行。

Stream遵循 “做什么,而不是怎么做”的原则。只需要描述需要做什么,而不用考虑怎样实现的。

集合主要是数据,stream流主要是计算。
只要给出需要对其包含的元素执行什么操作(做什么),比如“过滤掉长度大于几的字符串”、“获取不为null的元素“、“获取字符串(每个)的首字母”等,具体这些操作如何应用到每个元素上,就给Stream就好了

stream操作的三个步骤

新老实现的不同

package main.java.stream;

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

public class TestStream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("name");
        list.add("stream");
        list.add("lambda");
        list.add("filter");
        list.add("map");
        //1.我要取长度大于3的数据数量
        //原生写法
        long count = 0;
        for (String s : list) {
            if(s.length()>3){
                count++;
            }
        }
        System.out.println(count);
        //stream写法
        long countstream = list.stream().filter(s -> s.length()>3).count();
        System.out.println(countstream);
    }
}

剖析stream通用语法

简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第1张图片
红色框中的语句是一个Stream的生命开始的地方,负责创建一个Stream实例;绿色框中的语句是赋予Stream灵魂的地方,把一个Stream转换成另外一个Stream,红框的语句生成的是一个包含所有List变量的Stream,进过绿框的filter方法以后,重新生成了一个过滤掉原List列表所有长度大于3以后的Stream;蓝色框中的语句是聚合汇聚的地方,把Stream的里面包含的内容按照某种算法来汇聚成一个值
简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第2张图片

使用Stream的基本步骤

1.创建Stream
通过一个数据源获取一个流,例如,List中的stream()方法可以直接返回一个Stream对象。

2.转换Stream(中间操作)
转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换),我们需要对流中的数据进行的操作,比如循环处理(map),过滤(filter)等 可以 filter->map->…一系列链式操作

3.聚合(终止操作)
对Stream进行聚合(Reduce)操作,获取想要的结果;
流都是惰性求值的,这个我们在后面会讲到,需要进行一个终止操作,这样才会返回中间操作

详细解释Stream三个步骤

1.创建一个Stream。 list.stream() stream流的创建
2.在一个或多个步骤中将初始Stream船划到另一个Stream的中间操作。(filter(s -> s.length()>3).map().skip() … 这是一个链式操作 但是这个是延时执行的)
3.使用一个终止操作来产生结果。该操作会强制它之前的延时操作立即执行(就是第二部分 ),在这之后该Stream就不会在使用了(终止操作 .count())

创建Stream的几种方式

通过Stream以及Optional两个类,可以进一步利用函数式接口来简化代码。

1.通过Stream接口的静态工厂方法

1.1 of 通过具体值来创建流

通过Stream的静态方法Stream.of(T...values)可以创建一个流,它	
可以接受任意个值
Stream s = Stream.of("1",2,3.3,new String[]{"3", "4"});

1.2 iterate() generate() 无限流 函数创建无限流

Stream.iterate() 方法的第一个参数表示表示开始值(初始值), 第二个参数需要提供一个一元操作函数

1.2.1 generate() 生成一个无限长度的Stream
				generate()  需要一个供给型的函数式接口
			其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象)
       Stream stream21 = Stream.generate(new Supplier<String>() {
            @Override
            public String get() {
                return "asd";
           }
        });
        //Stream stream2 = Stream.generate(()->"asd");  无限流
        Stream stream2 = Stream.generate(()->"asd").limit(20); //如果不加limit() 终结,将会一直(循环)传递下去
        stream2.forEach(System.out::println);//双冒号就是把这个方法传入流内部然后每个元素都执行他

1.2.2 iterate()
 	Stream.iterate() 方法的第一个参数表示表示开始值(初始值),  第二个参数需要提供一个一元操作函数
 	和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
Stream stream1 = Stream.iterate("1",(x)->x+"好饿"+"1").limit(10);
1,1好饿1,1好饿1好饿1,....

2.通过Collection接口的默认方法(默认方法:Default method,是Java8中的一个新特性)–stream(),把一个Collection对象转换成Stream ,由Collection子类(List,Set)来创建流

list.stream();返回顺序流
list.parallelStream();//返回并行流

        Stream stream = list.stream();//返回顺序流
        Stream parallelStream = list.parallelStream();//返回并行流
        TreeSet<String> set = new TreeSet<>();
        Stream streamset=set.stream();//返回顺序流
        Stream parallelStreamset=set.parallelStream();//返回并行流

3.有数组创建流

数组可以通过Arrays工具类的stream方法来获得一个Steam对象
Arrays工具类的stream() 静态方法
Arrays.stream(array);

        //数组可以通过Arrays工具类的stream方法来获得一个Steam对象
        String[] array = new String[]{
          "a","b","c","d","e","f"
        };
        Stream streamarr = Arrays.stream(array);
        //还有许多重载形式的方法,可以返回带类型的Stream
        IntStream intStream = Arrays.stream(new int[]{1,2,3,4,5});

以下是详细的测试案例

package main.java.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class TestStream {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("name");
        list.add("stream");
        list.add("lambda");
        list.add("filter");
        list.add("map");
        //1.我要取长度大于3的数据数量
        //原生写法
        long count = 0;
        for (String s : list) {
            if(s.length()>3){
                count++;
            }
        }
        System.out.println(count);
        //stream写法
        long countstream = list.stream().filter(s -> s.length()>3).count();
        System.out.println(countstream);

        //创建Stream流的几种方法
        //1. 由Collection子类(List,Set)来创建流
        /**java8扩展了Collection接口,提供了stream(返回串行顺序流)
         * 和parallelStream(返回并行流)两个方法
         */
        Stream stream = list.stream();//返回顺序流
        Stream parallelStream = list.parallelStream();//返回并行流
        TreeSet<String> set = new TreeSet<>();
        Stream streamset=set.stream();//返回顺序流
        Stream parallelStreamset=set.parallelStream();//返回并行流

        //2.数组创建流
        //数组可以通过Arrays工具类的stream方法来获得一个Steam对象
        String[] array = new String[]{
          "a","b","c","d","e","f"
        };
        Stream streamarr = Arrays.stream(array);
        //还有许多重载形式的方法,可以返回带类型的Stream
        IntStream intStream = Arrays.stream(new int[]{1,2,3,4,5});

        //3.Stream接口的静态工厂方法 -- 通过具体值来创建流
        Stream s = Stream.of("1",2,3.3,new String[]{"3", "4"});
        //4.通过函数来创建流(无限流)
        //通过Stream.iterate()和Stream.generate()方法可以创建无限流
        //Stream.iterate() 方法的第一个参数表示表示开始值(初始值),  第二个参数需要提供一个一元操作函数
        //这里用 lambda表达式传递它
        //Stream stream1 = Stream.iterate("1",(x)->x+"好饿");  无限流
        Stream stream1 = Stream.iterate("1",(x)->x+"好饿"+"1").limit(10);
        //和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
        stream1.forEach(System.out::println);
        //Stream.generate() 需要一个供给型的函数式接口
        //其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象)
//        Stream stream21 = Stream.generate(new Supplier() {
//            @Override
//            public String get() {
//                return "asd";
//            }
//        });
        //Stream stream2 = Stream.generate(()->"asd");  无限流
        Stream stream2 = Stream.generate(()->"asd").limit(20); //如果不加limit() 终结,将会一直(循环)传递下去
        stream2.forEach(System.out::println);//双冒号就是把这个方法传入流内部然后每个元素都执行他

//        Stream.generate(new Supplier() {
//            @Override
//            public Double get() {
//                return Math.random();
//            }
//        });
        //Stream.generate(() -> Math.random());
        //Stream.generate(Math::random);
        //以上三种方式的作用都是一样的,只是使用了lambda表达式和方法引用的语法来简化代码

    }
}

Stream的中间操作(转换Stream):

1.转换Stream其实就是把一个Stream通过某些行为转换成一个新的Stream。
2.这种转化也就是stream的中间操作
3.Stream可以进行一系列的流水线式的中间操作,除非流水线上触发终止操作,否则,这些中间操作不会进行任何处理,而在终止操作时一次性处理,这个我们叫做Stream的惰性求值。
中间操作不管做多少次,都不会改变原来的流,只会返回一个新的流;

Stream接口中定义了几个常用的转换方法:

中间操作:筛选与切片(filter,limit,skip(n),distinct)

1.filter

filter(Predicate d) 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的stream 只包含符合条件的元素

简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第3张图片

2.distinct

distinct 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;
distinct 筛选元素,通过Stream元素中的hashCode和equals方法来去除重复元素
hashCode 与 equals 必须全都实现
不过缺点是去除的条件与hashCode值息息相关
简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第4张图片

    1.如果hashCode是name,age 一块儿,
      那么在equals中 用name比较,用age比较,
    2.用name&&age比较的,
      distinct中都是去找name和age都一样的去重
    3.如果hashCode是name或者 age 中的一个 单个的,
       就 equals 只实现 其中的一个name 或者 age 的判断
    4.distinct 就按照单属性来去重,但是不能去重多个
    5.也就是说distinct 多用来对象去重,或者基本类型去重,
      因为String 重写过所以不用考虑
    6.属性去重一般推荐下面的方法

例子实体 不包含hashCode equals重写

package main.java.stream;

import java.util.Objects;

public class TestBean implements Comparable<TestBean> {
    private String name;
    private Double salary;
    private String address;
    private int age;

    public TestBean(String name, Double salary, String address, int age) {
        this.name = name;
        this.salary = salary;
        this.address = address;
        this.age = age;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public int compareTo(TestBean o) {
        return ((this.getSalary()- o.getSalary()==0)?0:(this.getSalary()-o.getSalary()>0?1:-1));
    }

    @Override
    public String toString() {
        return "TestBean{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", address='" + address + '\'' +
                ", age=" + age +
                '}';
    }
}

hashCode 和 equals 重写


   @Override
   public boolean equals(Object obj) {
       TestBean bean = (TestBean)obj;
       //如果hashCode是name,age 一块儿,那么在equals中 用name比较,用age比较,
       // 用name&&age比较的,distinct中都是去找name和age都一样的去重
       //如果hashCode是name或者 age 中的一个 单个的,就 equals 只实现 其中的一个name 或者 age 的判断
       //distinct 就按照单属性来去重,但是不能去重多个
       //也就是说distinct 多用来对象去重,或者基本类型去重,因为String 重写过所以不用考虑
       //属性去重一般推荐下面的方法
       if(name.equals(bean.getName()) && age == bean.getAge()) return true;
       return super.equals(obj);
   }

   @Override
   public int hashCode() {
       //根据name属性返回hashCode
 //      return Objects.hash(name);
       //根据age返回hashCode
//        return Objects.hash(age);
       //根据 name&&age 返回hashCode
       return Objects.hash(name,age);

   }

3.distinctByKey

根据元素属性去重不写重写 hashCode equals distinctByKey
考虑distinct做一个有状态过滤器。这是一个函数,该函数返回一个谓词,该谓词保持先前状态的状态,并返回是否第一次看到给定元素:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}

然后您可以编写:

emps.stream().filter(distinctByKey(k -> k.getName())).filter(distinctByKey(TestBean::getAge)).forEach(System.out::println);

请注意,如果流是有序的并并行运行,则它将保留重复项中的任意元素,而不是第一个重复元素distinct()。

实现代码


    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }

    @Test
    public void test3() {
        List<TestBean> emps = Arrays.asList(
                new TestBean("赵一",789.11,"a",23),
                new TestBean("钱二",345.23,"b",56),
                new TestBean("钱二",2345.23,"c",56),
                new TestBean("孙三",789.324,"d",45),
                new TestBean("李四",9087.34,"e",23),
                new TestBean("王五",3456.34,"f",54)
        );
        List<String> sl = Arrays.asList(
               "name",
               "name",
               "test",
               "obj",
               "name"
        );

        //filter
        emps.stream().filter((e)->e.getAge()<54).forEach(System.out::println);
        System.out.println("============重写 hashCode 和 equals 方法=============");
        //distinct -- 重写 hashCode 和 equals 方法
        emps.stream().distinct().forEach(System.out::println);
        System.out.println("++++++++filter(distinctByKey)  去重+++++++++++");
        //filter(distinctByKey)  去重
        emps.stream().filter(distinctByKey(k -> k.getName())).filter(distinctByKey(TestBean::getAge)).forEach(System.out::println);

    }

运行结果:

TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}
============ 重写 hashCode 和 equals 方法=============
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}
++++++++filter(distinctByKey) 去重+++++++++++
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}

4. limit

limit(long maxSize) 截断流,使元素不超过manSize指定的数量
limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素

简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第5张图片

        System.out.println("++++++++limit(long maxSize)+++++++++++");
        // limit(long maxSize)
        emps.stream().filter(distinctByKey(k -> k.getName())).filter(distinctByKey(TestBean::getAge)).limit(2).forEach(System.out::println);
        System.out.println(emps.stream().filter(distinctByKey(k -> k.getName())).filter(distinctByKey(TestBean::getAge)).limit(2).count());

++++++++limit(long maxSize)+++++++++++
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
2

5. skip

skip(Long n) 跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则会返回一个空流
skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第6张图片

        System.out.println("++++++++skip(long n)+++++++++++");
        // skip(long n)
        emps.stream().filter(distinctByKey(k -> k.getName())).filter(distinctByKey(TestBean::getAge)).skip(2).forEach(System.out::println);
        System.out.println(emps.stream().filter(distinctByKey(k -> k.getName())).filter(distinctByKey(TestBean::getAge)).limit(2).count());

++++++++skip(long n)+++++++++++
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}
2

中间操作:映射

map(Function f) 接受一个函数型接口作为参数,该函数会对流中的每个元素进行处理,返回处理后的流 mapToDouble(ToDoubleFunction f) 接口一个函数型接口作为参数,该函数会对流中的每个元素进行处理,并返回一个Double值,最终得到一个Stream
mapToInt(ToIntFunction f) 接口一个函数型接口作为参数,该函数会对流中的每个元素进行处理,并返回一个Int值,最终得到一个Stream
mapToLong(ToLongFunction f) 接口一个函数型接口作为参数,该函数会对流中的每个元素进行处理,并返回一个Long值,最终得到一个Stream
flatMap(Function f) 接受一个函数作为参数,将流中的每个值都转换成一个新流,最后再将这些流连接到一起

1.map mapToInt mapToLong mapToDouble

map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。
这个方法有三个对于原始类型的变种方法,
分别是:mapToInt,mapToLong和mapToDouble。
这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,
可以免除自动装箱/拆箱的额外消耗;
简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第7张图片

        System.out.println("++++++++map(x->ss)+++++++++++");
        // map(long n)
        emps.stream().map(x->x.getAge()).forEach(System.out::println);
        emps.stream().map(x->x.getSalary()).forEach(System.out::println);
        System.out.println("++++++++mapToInt(x->ss)+++++++++++");
        emps.stream().mapToInt(x->x.getAge()).forEach(System.out::println);
        System.out.println("++++++++mapToLong(x->ss)+++++++++++");
        emps.stream().mapToLong(x->x.getAge()).forEach(System.out::println);
        System.out.println("++++++++mapToDouble(x->ss)+++++++++++");
        emps.stream().mapToDouble(x->x.getSalary()).forEach(System.out::println);

++++++++map(x->ss)+++++++++++
23
56
56
45
23
54
789.11
345.23
2345.23
789.324
9087.34
3456.34
++++++++mapToInt(x->ss)+++++++++++
23
56
56
45
23
54
++++++++mapToLong(x->ss)+++++++++++
23
56
56
45
23
54
++++++++mapToDouble(x->ss)+++++++++++
789.11
345.23
2345.23
789.324
9087.34
3456.34

2.flatMap flatMapToInt flatMapToLong flatMapToDouble

flatMap(Function f) 接受一个函数作为参数,将流中的每个值都转换成一个新流,最后再将这些流连接到一起
和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;
扁平化映射,它具体的操作是将多个stream连接成一个stream,这个操作是针对类似多维数组的,比如集合里面包含集合,相当于降维作用

简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第8张图片

        System.out.println("++++++++flatMap(x->ss)+++++++++++");
        List<List<TestBean>> list = new ArrayList<>();
        emps.stream().flatMap(x->Stream.of(x)).forEach(System.out::println);
        emps.stream().map(x->x.getName().split("")).forEach(System.out::println);
        emps.stream().flatMap(x->Stream.of(x.getName().split(""))).forEach(System.out::println);
        for (int i=0;i<2;i++){
            list.add(emps);
        }
        System.out.println("++++++++flatMap+++++++++++");
        list.stream().flatMap(x->x.stream().flatMap(e->Stream.of(e.getAge()))).forEach(System.out::println);
        System.out.println("++++++++flatMapToInt+++++++++++");
        list.stream().flatMapToInt(x->x.stream().flatMapToInt(e->IntStream.of(e.getAge()))).forEach(System.out::println);
        System.out.println("++++++++flatMapToLong+++++++++++");
        list.stream().flatMapToLong(x->x.stream().flatMapToLong(e-> LongStream.of(e.getAge()))).forEach(System.out::println);
        System.out.println("++++++++flatMapToDouble+++++++++++");
        list.stream().flatMapToDouble(x->x.stream().flatMapToDouble(e-> DoubleStream.of(e.getSalary()))).forEach(System.out::println);
        System.out.println("++++++++flatMap 升维+++++++++++");
        List<Integer> numbers1 = Arrays.asList(1,2,3,4);
        List<Integer> numbers2 = Arrays.asList(5,6);
        numbers1.stream().flatMap(x->numbers2.stream().map(y->new int[]{x,y})).collect(Collectors.toList()).forEach(d->System.out.println(Arrays.toString(d)));
        System.out.println("++++++++flatMap 降维+++++++++++");
        list.stream().flatMap(x->x.stream()).forEach(System.out::println);

++++++++flatMap+++++++++++
23
56
56
45
23
54
23
56
56
45
23
54
++++++++flatMapToInt+++++++++++
23
56
56
45
23
54
23
56
56
45
23
54
++++++++flatMapToLong+++++++++++
23
56
56
45
23
54
23
56
56
45
23
54
++++++++flatMapToDouble+++++++++++
789.11
345.23
2345.23
789.324
9087.34
3456.34
789.11
345.23
2345.23
789.324
9087.34
3456.34
++++++++flatMap 升维+++++++++++
[1, 5]
[1, 6]
[2, 5]
[2, 6]
[3, 5]
[3, 6]
[4, 5]
[4, 6]
++++++++flatMap 降维+++++++++++
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}

中间操作:排序

描述 sorted 返回一个新流,流中的元素按照自然排序进行排序 sorted(Comparator comp) 返回一个新流,并且Comparator指定的排序方式进行排序

sorted ()
       System.out.println("++++++++sorted() --Comparable +++++++++++");
        //1.按照自然排序,注意,需要进行自然排序则对象必须实现Comparable接口
        emps.stream().sorted().forEach(System.out::println);
        System.out.println("++++++++sorted() --  +++++++++++");
        emps.stream().map(x->x.getName()).sorted().forEach(System.out::println);
        System.out.println("++++++++sorted() ==  +++++++++++");
        emps.stream().map(TestBean::getAge).sorted().forEach(System.out::println);

++++++++sorted() --Comparable +++++++++++
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘钱二’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}
++++++++sorted() – +++++++++++
孙三
李四
王五
赵一
钱二
钱二
++++++++sorted() == +++++++++++
23
23
45
54
56
56

sorted(Comparator comp)
        //2.按照给定规则进行排序,(按照工资高低进行排序)
        emps.stream().sorted((x,y)-> {
            if(x.getSalary() == y.getSalary()){
                return 0;
            }else{
                return x.getSalary()>y.getSalary()?-1:1;
            }
        }).forEach(System.out::println);
        System.out.println("++++++++sorted(comparetor comp2 ) ==  +++++++++++");
        emps.stream().sorted((x, y) -> Double.compare(x.getSalary(), y.getSalary())).forEach(System.out::println);

++++++++sorted(comparetor comp1 ) = +++++++++++
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}
TestBean{name=‘钱二’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
++++++++sorted(comparetor comp2 ) == +++++++++++
TestBean{name=‘钱二’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘赵一’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘孙三’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘钱二’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘王五’, salary=3456.34, address=‘f’, age=54}
TestBean{name=‘李四’, salary=9087.34, address=‘e’, age=23}

中间操作消费函数 peek(消费函数)

peek()

peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的void的消费函数;
peek 操作虽然与foreach操作很想,但是这里内部有很多问题
例如 仅在对流内元素进行操作时,peek才会被调用,当不对元素做任何操作时,peek自然也不会被调用了

该方法主要用于调试,方便debug查看Stream内进行处理的每个元素。
jdk 10 后如果用到 count() peek()不会执行

简单全面学习JDK1.8新特性之流式编程-SreamAPI(二)_第9张图片

       //peek
       System.out.println("++++++++peek =  +++++++++++");
       emps.stream().peek((x)-> x.setName(x.getName()+"123")).forEach(System.out::println);
       System.out.println("++++++++peek =1  +++++++++++");
       System.out.println(emps.stream().peek(System.out::println).count());

++++++++peek = +++++++++++
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
++++++++peek =1 +++++++++++
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
6

        //peek 操作虽然与foreach操作很想,但是这里内部有很多问题
        //例如 仅在对流内元素进行操作时,peek才会被调用,当不对元素做任何操作时,peek自然也不会被调用了
        //该方法主要用于调试,方便debug查看Stream内进行处理的每个元素。
        //jdk 10 后如果用到 count()  peek()不会执行
        System.out.println("peek debug");
        emps.stream().peek(TestStream::debug).count();
        IntStream.range(1,10).peek(System.out::println).count();
    public static void debug(TestBean i){
        System.out.println(Thread.currentThread().getName() + " " + "debug"+i.getName()+"ff" + i.toString());
        try {
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

peek debug
main debug赵一123ffTestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
main debug钱二123ffTestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
main debug钱二123ffTestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
main debug孙三123ffTestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
main debug李四123ffTestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
main debug王五123ffTestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
1
2
3
4
5
6
7
8
9

性能问题

        //链式操作
        List<Integer> nums = Arrays.asList(1,1,null,2,3,4,null,5,6,7,8,9,10);
        System.out.println("sum is:"+nums.stream().filter(em -> em != null).
        distinct().mapToInt(num -> num * 2).peek(System.out::println).skip(2).limit(4).sum());

链式操作
2
4
6
8
10
12
sum is:36

有些细心的同学可能会有这样的疑问:在对于一个Stream进行多次转换操作,每次都对Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是一个for循环里把所有操作都做掉的N(转换的次数)倍啊?
其实不是这样的,转换操作都是lazy的,多个转换操作只会在汇聚操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在汇聚操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数

终止操作

遍历

forEach     并行流里面顺序随机
forEachOrdered  并行流里面严格按照顺序  

1.使用stream流,这个是一个串行流,也就是程序是串行执行的,所有看到遍历的结果都是按照集合的元素放入的顺序
2.使用的parallelStream的流,这个流表示一个并行流,也就是在程序内部迭代的时候,会帮你免费的并行处理
3。在并行中forEachOrdered表示严格按照顺序取数据,forEach在并行中,随机排列了;这个也可以看出来,在并行的程序中,如果对处理之后的数据,没有顺序的要求,使用forEach的效率,肯定是要更好的

        System.out.println("==========this is forEach in serial=======");
        //遍历流中的元素 串行流
        emps.stream().forEach(x->{
            if(x.getAge()>40)
            System.out.println(x.toString());
        });
        System.out.println("==========this is forEachOrdered in serial=======");
        //遍历流中的元素 串行流
        emps.stream().forEachOrdered(x->{
            if(x.getAge()>40)
            System.out.println(x.toString());
        });
        System.out.println("==========this is forEach in parallel=======");
        //遍历流中的元素 并行流 随机排列   速度快
        emps.parallelStream().forEach(x->{
            if(x.getAge()>40)
            System.out.println(x.toString());
        });
        System.out.println("==========this is forEachOrdered in parallel=======");
        //遍历流中的元素 并行流 严格按照流中的顺序
        emps.parallelStream().forEachOrdered(x->{
            if(x.getAge()>40)
                System.out.println(x.toString());
        });

====this is forEach in serial=
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
====this is forEachOrdered in serial=
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
====this is forEach in parallel=
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
====this is forEachOrdered in parallel=
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}

查找与匹配

 allMatch -- 检查是否匹配所有元素
 anyMatch -- 检查是否至少匹配一个元素
 noneMatch -- 检查是否没有匹配的元素
 findFirst -- 返回第一个元素
 findAny -- 返回当前流中的任意元素
 count-- 返回流中元素的总个数
 max-- 返回流中最大值
 min -- 返回流中最小值
 toArray - 将流返回成数组
        //终端操作- 终止操作
        //allMatch
        System.out.println("==========this is allMatch=======");
        //检查是否有匹配的元素
        System.out.println(emps.stream().peek(System.out::println).allMatch(x -> x.getAge() > 30));
        System.out.println("==========this is anyMatch=======");
        //检查是否至少有一个匹配的元素
        System.out.println(emps.stream().peek(System.out::println).anyMatch(x->x.getAge()>30));
        System.out.println("==========this is noneMatch=======");
        //检查是否没有匹配的元素
        System.out.println(emps.stream().peek(System.out::println).noneMatch(x -> x.getAge() > 30));
        System.out.println("==========this is findFirst=======");
        //返回第一个元素
        System.out.println(emps.stream().peek(System.out::println).findFirst());
        System.out.println("==========this is findAny=======");
        //返回当前流中的任意元素
        System.out.println(emps.stream().peek(System.out::println).findAny());
        System.out.println("==========this is max=======");
        //返回流中最大值
        Optional<TestBean> max = emps.stream().peek(System.out::println).max((x,y)->x.getAge()-y.getAge());
        System.out.println(max.get());
        System.out.println("==========this is min=======");
        //返回流中最小值
        System.out.println(emps.stream().peek(System.out::println).min((x,y)->x.getAge()-y.getAge()));
        System.out.println("==========this is count=======");
        //返回流元素个数
        System.out.println(emps.stream().peek(System.out::println).count());
  System.out.println("==========this is toArray=======");
        //toArray
        System.out.println(emps.parallelStream().toArray().length);

链式操作
2
4
6
8
10
12
sum is:36
====this is allMatch=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
false
====this is anyMatch=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
true
====this is noneMatch=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
false
====this is findFirst=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
Optional[TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}]
====this is findAny=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
Optional[TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}]
====this is max=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
====this is min=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
Optional[TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}]
====this is count=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
6
====this is toArray=
6

终止操作:汇聚(Reduce)&收集(Collect)Stream

reduce()
collect()
内容比较多,会将这块内容单独放在第三篇中讲述
链接: 简单全面学习java8新特性Stream-API中的Reduce&Collect(三).

其他

concat 合并流

        System.out.println("==========this is concat=======");
        Stream.concat(emps.stream(),Stream.of(1,2,3,4,5)).forEach(System.out::println);

====this is concat=
TestBean{name=‘赵一123’, salary=789.11, address=‘a’, age=23}
TestBean{name=‘钱二123’, salary=345.23, address=‘b’, age=56}
TestBean{name=‘钱二123’, salary=2345.23, address=‘c’, age=56}
TestBean{name=‘孙三123’, salary=789.324, address=‘d’, age=45}
TestBean{name=‘李四123’, salary=9087.34, address=‘e’, age=23}
TestBean{name=‘王五123’, salary=3456.34, address=‘f’, age=54}
1
2
3
4
5

综合整理

Stream对象提供多个非常有用的方法,这些方法可以分成两类:
中间操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
终端操作:产生的是一个结果或者其它的复合操作;如count或者forEach操作。

常见中间操作:

方法 说明
sequential 返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象
parallel 返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象
unordered 返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象
onClose 返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象
close 关闭Stream对象
filter 元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅包含未被过滤的元素
map 元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的结果
mapToInt 元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象
flatMap 元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回;
distinct 去重:返回一个去重后的Stream对象
sorted 排序:返回排序后的Stream对象
peek 使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象
limit 获取有限个元素组成新的Stream对象返回
skip 抛弃前指定个元素后使用剩下的元素组成新的Stream返回
takeWhile 如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。
dropWhile 与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。

常见终端操作 :

方法 说明
iterator 返回Stream中所有对象的迭代器;
spliterator 返回对所有对象进行的spliterator对象
forEach 对所有元素进行迭代处理,无返回值
forEachOrdered 按Stream的Encounter所决定的序列进行迭代处理,无返回值
toArray 返回所有元素的数组
reduce 使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。
collect 根据传入参数做相关汇聚计算
min 返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty
max 与Min相反
count 所有元素个数
anyMatch 只要其中有一个元素满足传入的Predicate时返回True,否则返回False
allMatch 所有元素均满足传入的Predicate时返回True,否则False
noneMatch 所有元素均不满足传入的Predicate时返回True,否则False
findFirst 返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。
findAny 返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。
isParallel 判断是否当前Stream对象是并行的

Optional

用于简化Java中对空值的判断处理,以防止出现各种空指针异常。
Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。
它的构造函数都是private类型的,因此要初始化一个Optional的对象无法通过其构造函数进行创建。它提供了一系列的静态方法用于构建Optional对象:

empty

用于创建一个空的Optional对象;其value属性为Null。
如:

Optional o = Optional.empty();

of

根据传入的值构建一个Optional对象;
传入的值必须是非空值,否则如果传入的值为空值,则会抛出空指针异常。
使用:


o = Optional.of("test"); 

ofNullable

根据传入值构建一个Optional对象
传入的值可以是空值,如果传入的值是空值,则与empty返回的结果是一样的。

常用方法:

方法 说明
get 获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异常;因此返回的Value值无需再做空值判断,只要没有抛出异常,都会是非空值。
isPresent Value是否为空值的判断;
ifPresent 当Value不为空时,执行传入的Consumer;
ifPresentOrElse Value不为空时,执行传入的Consumer;否则执行传入的Runnable对象;
filter 当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回当前的Optional对象
map 一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的结果组装的Optional对象;
flatMap 一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的结果(其返回结果直接是Optional对象)
or 如果Value不为空,则返回当前的Optional对象;否则,返回传入的Supplier生成的Optional对象;
stream 如果Value为空,返回Stream对象的Empty值;否则返回Stream.of(value)的Stream对象;
orElse Value不为空则返回Value,否则返回传入的值;
orElseGet Value不为空则返回Value,否则返回传入的Supplier生成的值;
orElseThrow Value不为空则返回Value,否则抛出Supplier中生成的异常对象;

你可能感兴趣的:(java8新的理解,学习教程,基础知识)