Java Stream流使用语法详解

如何使用Stream?

聚合操做是Java 8针对集合类,使编程更为便利的方式,能够与Lambda表达式一块儿使用,达到更加简洁的目的。java

前面例子中,对聚合操做的使用能够归结为3个部分:编程

1)  建立Stream:经过stream()方法,取得集合对象的数据集。
       2)  Intermediate:经过一系列中间(Intermediate)方法,对数据集进行过滤、检索等数据集的再次处理。如上例中,使用filter()方法来         对数据集进行过滤。
       3)  Terminal经过最终(terminal)方法完成对数据集中元素的处理。如上例中,使用forEach()完成对过滤后元素的打印。
       在一次聚合操做中,能够有多个Intermediate,可是有且只有一个Terminal。也就是说,在对一个Stream能够进行屡次转换操做,并非每次都对Stream的每一个元素执行转换。并不像for循环中,循环N次,其时间复杂度就是N。转换操做是lazy(惰性求值)的,只有在Terminal操做执行时,才会一次性执行。能够这么认为,Stream 里有个操做函数的集合,每次转换操做就是把转换函数放入这个集合中,在 Terminal 操做的时候循环 Stream 对应的集合,而后对每一个元素执行全部的函数。数组

Stream的操做分类
刚才提到的Stream的操做有Intermediate、Terminal和Short-circuiting:并发

  • Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered
  • Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
  • Short-circuiting: anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

一、建立流

若是是数组的话,可使用 Arrays.stream() 或者 Stream.of() 建立流;若是是集合的话,能够直接使用 stream() 方法建立流,由于该方法已经添加到 Collection 接口中。ide

public class CreateStreamDemo {
    public static void main(String[] args) {
        String[] arr = new String[]{"武汉加油", "中国加油", "世界加油"};
        Stream<String> stream = Arrays.stream(arr);

        stream = Stream.of("武汉加油", "中国加油", "世界加油");

        List<String> list = new ArrayList<>();
        list.add("武汉加油");
        list.add("中国加油");
        list.add("世界加油");
        stream = list.stream();
    }
}

查看 Stream 源码的话,你会发现 of() 方法内部其实调用了 Arrays.stream() 方法。函数

public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}


另外,集合还能够调用 parallelStream() 方法建立并发流,默认使用的是 ForkJoinPool.commonPool()线程池。工具

List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();

二、操做流


Stream 类提供了不少有用的操做流的方法,我来挑一些经常使用的给你介绍一下。ui

1)过滤线程

经过 filter() 方法能够从流中筛选出咱们想要的元素。code

public class FilterStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");
        Stream<String> stream = list.stream().filter(element -> element.contains("王"));
        stream.forEach(System.out::println);
    }
}

filter() 方法接收的是一个 Predicate(Java 8 新增的一个函数式接口,接受一个输入参数返回一个布尔值结果)类型的参数,所以,咱们能够直接将一个 Lambda 表达式传递给该方法,好比说 element -> element.contains("王") 就是筛选出带有“王”的字符串。

forEach() 方法接收的是一个 Consumer(Java 8 新增的一个函数式接口,接受一个输入参数而且无返回的操做)类型的参数,类名 :: 方法名是 Java 8 引入的新语法,System.out 返回 PrintStream 类,println 方法你应该知道是打印的。

stream.forEach(System.out::println); 至关于在 for 循环中打印,相似于下面的代码:

for (String s : strs) {
    System.out.println(s);
}

很明显,一行代码看起来更简洁一些。来看一下程序的输出结果:

王力宏

2)映射

若是想经过某种操做把一个流中的元素转化成新的流中的元素,可使用 map() 方法。

public class MapStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");
        Stream<Integer> stream = list.stream().map(String::length);
        stream.forEach(System.out::println);
    }
}

map() 方法接收的是一个 Function(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R)类型的参数,此时参数 为 String 类的 length 方法,也就是把 Stream<String> 的流转成一个 Stream<Integer> 的流。

程序输出的结果以下所示:

3
3
2
3

3)匹配

Stream 类提供了三个方法可供进行元素匹配,它们分别是:

anyMatch(),只要有一个元素匹配传入的条件,就返回 true。

allMatch(),只有有一个元素不匹配传入的条件,就返回 false;若是所有匹配,则返回 true。

noneMatch(),只要有一个元素匹配传入的条件,就返回 false;若是所有匹配,则返回 true。

public class MatchStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");

        boolean  anyMatchFlag = list.stream().anyMatch(element -> element.contains("王"));
        boolean  allMatchFlag = list.stream().allMatch(element -> element.length() > 1);
        boolean  noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉"));
        System.out.println(anyMatchFlag);
        System.out.println(allMatchFlag);
        System.out.println(noneMatchFlag);
    }
}


由于“王力宏”以“王”字开头,因此 anyMatchFlag 应该为 true;由于“周杰伦”、“王力宏”、“陶喆”、“林俊杰”的字符串长度都大于 1,因此 allMatchFlag 为 true;由于 4 个字符串结尾都不是“沉”,因此 noneMatchFlag 为 true。

程序输出的结果以下所示:

true
true
true

4)组合

reduce() 方法的主要做用是把 Stream 中的元素组合起来,它有两种用法:

  • Optional<T> reduce(BinaryOperator<T> accumulator)

没有起始值,只有一个参数,就是运算规则,此时返回 Optional。

  • T reduce(T identity, BinaryOperator<T> accumulator)

有起始值,有运算规则,两个参数,此时返回的类型和起始值类型一致。

来看下面这个例子。

public class ReduceStreamDemo {
    public static void main(String[] args) {
        Integer[] ints = {0, 1, 2, 3};
        List<Integer> list = Arrays.asList(ints);

        Optional<Integer> optional = list.stream().reduce((a, b) -> a + b);
        Optional<Integer> optional1 = list.stream().reduce(Integer::sum);
        System.out.println(optional.orElse(0));
        System.out.println(optional1.orElse(0));

        int reduce = list.stream().reduce(6, (a, b) -> a + b);
        System.out.println(reduce);
        int reduce1 = list.stream().reduce(6, Integer::sum);
        System.out.println(reduce1);
    }
}

运算规则能够是 Lambda 表达式(好比 (a, b) -> a + b),也能够是类名::方法名(好比 Integer::sum)。

程序运行的结果以下所示:

6
6
12
12

0、一、二、3 在没有起始值相加的时候结果为 6;有起始值 6 的时候结果为 12。

三、转换流0


既然能够把集合或者数组转成流,那么也应该有对应的方法,将流转换回去——collect() 方法就知足了这种需求。

public class CollectStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");

        String[] strArray = list.stream().toArray(String[]::new);
        System.out.println(Arrays.toString(strArray));

        List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
        List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
        System.out.println(list1);
        System.out.println(list2);

        String str = list.stream().collect(Collectors.joining(", ")).toString();
        System.out.println(str);
    }
}

toArray() 方法能够将流转换成数组,你可能比较好奇的是 String[]::new,它是什么东东呢?来看一下 toArray() 方法的源码。

<A> A[] toArray(IntFunction<A[]> generator);

也就是说 String[]::new 是一个 IntFunction,一个能够产生所需的新数组的函数,能够经过反编译字节码看看它究竟是什么:

String[] strArray = (String[])list.stream().toArray((x$0) -> {
    return new String[x$0];
});
System.out.println(Arrays.toString(strArray));

也就是至关于返回了一个指定长度的字符串数组。

当咱们须要把一个集合按照某种规则转成另一个集合的时候,就能够配套使用 map() 方法和 collect() 方法。

List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());

经过 stream() 方法建立集合的流后,再经过 map(String:length) 将其映射为字符串长度的一个新流,最后经过 collect() 方法将其转换成新的集合。

Collectors 是一个收集器的工具类,内置了一系列收集器实现,好比说 toList() 方法将元素收集到一个新的 java.util.List 中;好比说 toCollection() 方法将元素收集到一个新的 java.util.ArrayList 中;好比说 joining() 方法将元素收集到一个能够用分隔符指定的字符串中。

来看一下程序的输出结果:

[周杰伦, 王力宏, 陶喆, 林俊杰]
[3, 3, 2, 3]
[周杰伦, 王力宏, 陶喆, 林俊杰]
周杰伦, 王力宏, 陶喆, 林俊杰
相关文章
相关标签/搜索