概述

Stream API 把真正的函数式编程风格引入到java中,这是目前为止对JAVA类库最好的补充,因为其可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

其是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用其对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用其来并行执行操作。简言之,其提供了一种高效且易于使用的处理数据的方式。

StreamCollection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的,前者是主要面向内存,存储在内存中;后者主要面向CPU,通过CPU实现计算。

所以说Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

注意

  • Stream自己不会存储元素
  • Stream不会改变源对象,相反,他们会返回一个持有结果的新的Stream
  • Stream操作是延迟执行的,这意味着他们会等到需要结构的时候才执行

Stream操作的三个步骤

  • 创建Stream:一个数据源,获取一个流
  • 中间操作:中间操作链,对数据源的数据进行处理
  • 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果,之后,不会再被使用

实例化

Stream主要分为四种实例化方式:

  • 通过集合 Stream
default Stream<E> parallelStream() 返回一个并行流,类似于线程,并行执行中间链
default Stream<E> stream() 返回一个顺序流,顺序执行中间链
  • 通过数组 Stream
调用的是Arrays类的static <T> Stream<T> stream(<T>[] array)
  • 通过Stream.of()方法
  • 创建无限流

示例:

public class StreamNewTest {

    /** 通过集合创建Stream */
    public static void create1(){
        //获取一个集合
        List<User> users = UserData.getUsersData();
        //default Stream<E> stream() 返回一个顺序流,顺序执行中间链
        Stream<User> stream = users.stream();
        //default Stream<E> parallelStream() 返回一个并行流,类似于线程,并行执行中间链
        Stream<User> parallelStream = users.parallelStream();
    }

    /** 通过数组创建Stream */
    public static void create2(){
        //获取一个数组,静态初始化
        int[] arr = new int[]{1,2,3,4,5,6};
        //调用的是Arrays类的static <T> Stream<T> stream(<T>[] array)
        IntStream s1 = Arrays.stream(arr);
        //自定义数组
        User[] arr2 = new User[]{new User("李四","28"),new User("赵六","28")};
        Stream<User> s2 = Arrays.stream(arr2);
    }

    /** 通过Stream.of()创建Stream */
    public static void create3(){
        //不是数组形式,以包装类的形式
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
    }

    /** 创建Stream无限流 */
    public static void create4(){
        //迭代 static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
        //生成 static<T> Stream<T> generate(Supplier<T> s)
        Stream.generate(()->2).limit(10).forEach(System.out::println);
    }

}

切片与筛选

方法

描述

filter[Predicate p]

接收Lambda,从流中排除某些元素

distinct()

筛选,通过流所生成元素的hashCode()和equals()去除重复元素

limit(long maxSize)

截断流,使其元素不超过给定数量

skip(long n)

跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,返回一个空流,与limit()互补

示例:

/** 切片与筛选 */
public static void test1(){
    List<User> list = UserData.getUsersData();
    //filter(Predicate p) 筛选
    list.stream().filter(u -> Integer.parseInt(u.getAge()) > 20).forEach(System.out::println);
    //distinct() 去重
    list.stream().distinct().forEach(System.out::println);
    //limit(long maxSize) 切片,截取流的前几个元素
    list.stream().limit(2).forEach(System.out::println);
    //skip(long n) 切片,跳过流的前n个元素
    list.stream().skip(2).forEach(System.out::println);
}

映射

方法

描述

map(Function f)

接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

mapToDouble(ToDoubleFunction f)

接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream

mapToInt(ToIntFunction f)

接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream

mapToLong(ToLongFunction f)

接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream

flatMap(Function f)

接收一个函数作为参数,将流中的每个值都被换成另一个流,然后把所有流连接成一个流。

示例:

/** 映射 */
public static void test2(){
    //map(Function f) 遍历每个元素,并将其映射成一个新的元素
    List<String> list = Arrays.asList("20", "1", "2", "3");
    list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
    //mapToInt(ToIntFunction f);
    list.stream().mapToInt(str -> Integer.parseInt(str)).forEach(System.out::println);

    //flatMap(Function f) 流中的每个值都被换成另一个流,然后把所有流连接成一个流
    list.stream().flatMap(StreamApiTest::fromStrToStream).forEach(System.out::println);
    //对比map方法实现
    Stream<Stream<Character>> streamStream = list.stream().map(StreamApiTest::fromStrToStream);
    streamStream.forEach(s -> {
        s.forEach(System.out::println);
    });

}

public static Stream<Character> fromStrToStream(String str){
    ArrayList<Character> list = new ArrayList<>();
    for(Character c :str.toCharArray()){
        list.add(c);
    }
    return list.stream();
}

排序

方法

描述

sorted()

产生一个新流,其中按自然顺序排序

sorted(Comparator com)

产生一个新流,其中按比较器顺序排序

示例:

/** 排序 */
public static void test3(){
    List<User> list = UserData.getUsersData();
    List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6);
    //自然顺序排序
    list1.stream().sorted().forEach(System.out::println);
    //按比较器顺序排序,对象排序需要是实现Comparator接口或者如下
    list.stream().sorted((u1,u2) -> Integer.compare(Integer.parseInt(u1.getAge()),Integer.parseInt(u2.getAge()))).forEach(System.out::println);
}

匹配与查找

方法

描述

allMatch(Predicate p)

检查是否匹配所有元素

anyMatch(Predicate p)

检查是否至少匹配一个元素

noneMatch(Predicate p)

检查是否没有匹配所有元素

findFirst()

返回第一个元素

findAny(()

返回当前流中的任意元素

count()

统计总数

max(Comparator c)

返回流中最大值

min(Comparator c)

返回流中最小值

forEach(Consumer c)

内部迭代

示例:

/** 匹配与查找 */
public static void test4(){
    List<User> list = UserData.getUsersData();
    //allMatch(Predicate p):断定型判断,返回布尔类型,流中是否所有用户都是50岁以上
    boolean b = list.stream().allMatch(u -> Integer.parseInt(u.getAge()) > 50);
    //anyMatch(Predicate p):断定型判断,返回布尔类型,流中是否存在用户是50岁以上
    boolean b1 = list.stream().anyMatch(u -> Integer.parseInt(u.getAge()) > 50);
    //anyMatch(Predicate p):断定型判断,返回布尔类型,流中是否不存在用户是50岁以上
    boolean b2 = list.stream().noneMatch(u -> Integer.parseInt(u.getAge()) > 50);
    //findFirst():返回第一个元素
    Optional<User> u = list.stream().findFirst();
    //findAny():返回任一个元素
    Optional<User> u1 = list.stream().findAny();
    //count():统计总数
    long count = list.stream().count();
    //max(Comparator c):返回流中最大值
    Optional<User> maxUser = list.stream().max((o1, o2) -> Integer.compare(Integer.parseInt(o1.getAge()), Integer.parseInt(o2.getAge())));
    //min(Comparator c):返回流中最小值
    Optional<User> minUser = list.stream().min((o1, o2) -> Integer.compare(Integer.parseInt(o1.getAge()), Integer.parseInt(o2.getAge())));
    //forEach(Consumer c):内部迭代
    list.stream().forEach(System.out::println);
}

归约

方法

描述

reduce(T iden,BinaryOPerator b)

可以将流中元素反复结合起来,得到一个值,返回T

reduce(BinaryOPerator b)

可以将流中元素反复结合起来,得到一个值,返回Optional

示例:

/** 归约 */
public static void test5(){
    //reduce(T iden, BinaryOperator b):计算总和
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    Integer reduce = list.stream().reduce(0, Integer::sum);
    //reduce(BinaryOperator b):计算所有用户的总年龄
    List<User> uList = UserData.getUsersData();
    Optional<User> reduce1 = uList.stream().reduce((u1, u2) -> new User("", Integer.parseInt(u1.getAge()) + Integer.parseInt(u2.getAge())+""));
    //reduce(BinaryOperator b):计算所有用户的总年龄2--->map-reduce
    OptionalInt reduce2 = uList.stream().mapToInt(u -> Integer.parseInt(u.getAge())).reduce(Integer::sum);
}

收集

方法

描述

collector(Collector c)

将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

示例:

/** 收集 */
public static void test6(){
    List<User> uList = UserData.getUsersData();
    //不使用collector返回的是一个流
    Stream<User> userStream = uList.stream().filter(u -> Integer.parseInt(u.getAge()) > 50);
    //使用collector返回的是一个List
    List<User> users = uList.stream().filter(u -> Integer.parseInt(u.getAge()) > 50).collect(Collectors.toList());
}

Collectors方法简单介绍:

方法

描述

toCollection

将流中的元素全部放置到一个集合中返回

toList

将流中的元素放置到一个列表集合

toSet

将流中的元素放置到一个无序集set

joining

将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀

mapping

这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳

collectingAndThen

在归纳动作结束之后,对归纳的结果进行再处理

counting

用于计数

minBymaxBy

生成一个用于获取最小/最大值的Optional结果的Collector

summingIntsummingLong

summingDouble

求和,除此外还包含求平均数等

toMap

根据给定的键生成器和值生成器生成的键和值保存到一个map中返回

groupingBy

生成一个拥有分组功能的Collector

summarizingIntsummarizingLong

summarizingDouble

适用于汇总的,返回值分别是IntSummaryStatisticsLongSummaryStatisticsDoubleSummaryStatistics包含有流中元素的指定结果的数量、和、最大值、最小值、平均值