在了解了lambda之后,来看看Java 8引入的Stream结构。

几种不同的Stream类型

Java 8提供了几种不同的Stream类型,它们是,

  • Stream
  • IntStream
  • LongStream
  • DoubleStream

Stream用于处理通用类型数据,后三者为原生数据类型int、long、double创建的Stream。在从IntStream切换到Stream时要手动进行调用boxed方法来进行转化。Java 8并没有提供所有原生类型的Stream支持,所以剩下的几种原生类型在处理上就相对麻烦了一些。这里也吐槽下Java在原生类型与引用类型上的处理,有时使用起来忒麻烦。

Stream接口定义

容器上的常用函数式操作接口都在Stream接口中定义,来看看目前所支持的所有接口,

Stream

构建Stream的几种方法

Stream构建大致有如下几种方式,

从数组创建, Arrays.stream

Arrays.stream(new int[] {1, 1, 2, 3, 5});
Arrays.stream(new long[] {1, 1, 2, 3, 5});
Arrays.stream(new double[] {1, 1, 2, 3, 5});
Arrays.stream(new String[] {"foo", "bar"});

从数组创建Stream会有一个限制,Java 8原生提供了IntStream、LongStream、DoubleStream,所以可以直接从int、long、double数组构建,但是不能从boolean、char、float等其余原生类型数组构建Stream。

// compile error
Arrays.stream(new boolean[] {true, false});
Arrays.stream(new char[] {'f', 'o', 'o});

对于这部分类型数组只能转化成Boolean、Character、Float数组或是对应的容器类型之后再进行构建了。

从容器创建, Collection.stream

Arrays.asList(1, 2, 3).stream();

Stream上的静态方法

  • Stream.empty,构建空的Stream
Stream.empty();
  • Stream.of,从数据直接构建
Stream.of(1, 2, 3);
Stream.of(1);
  • Stream.concat,拼接Stream
Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5, 6))

创建infinite stream

Stream上另外有两个方法可以用于创建infinite stream,

  • Stream.generate
Stream.generate(Math::random).limit(10).forEach(System.out::println);

Stream.generate(new Supplier<Integer>() {
    private int start = 0;
    @Override
    public Integer get() {
        start += 1;
        return start;
    }
}).limit(10).forEach(System.out::println);
  • Stream.iterate
Stream.iterate(1, x -> x + 1).limit(10).forEach(System.out::println);

因为创建的是infinte stream,所以需要配合limit函数进行操作,否则在调用forEach时会陷入无限循环。

Stream接口代码示例

一一试用Stream提供的接口,

  • forEach, 遍历并进行操作
List<String> words = Arrays.asList("foo", "bar");
words.stream().forEach(System.out::println);
  • forEachOrdered, 有序遍历进行操作
List<String> words = Arrays.asList("foo", "bar");
words.stream().forEachOrdered(System.out::println);
  • allMatch,判断是否每一个元素都满足条件
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().allMatch(x -> x > 0);
  • anyMatch,判断是否有一个元素满足条件
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().anyMatch(x -> x > 3);
  • noneMatch,判断是否没有元素满足条件
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().noneMatch(x -> x > 5);
  • skip,跳过Stream开头的指定个数元素
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().skip(2).forEach(System.out::println);
  • limit,保留指定个数元素
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().limit(4).forEach(System.out::println);
  • findFirst,返回首个元素
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().findFirst().ifPresent(System.out::println);
  • findAny,返回任意一个元素
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().findAny().ifPresent(System.out::println);
  • distinct,返回不包含重复元素的Stream
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().distinct().forEach(System.out::println);
  • count,统计Stream中元素个数
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().count();
  • max,获取最大元素
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().max((x, y) -> x - y).ifPresent(System.out::println);
  • min,获取最小元素
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().min((x, y) -> x - y).ifPresent(System.out::println);
  • sorted,排序
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
// 自然排序
nums.stream().sorted().forEach(System.out::println);
// 通过comparator指定排序规则
nums.stream().sorted((x, y) -> y - x).forEach(System.out::println);
  • map,对每一个元素进行转换,生成新的Stream
List<String> words = Arrays.asList("foo", "bar"); 
words.stream().map(String::toUpperCase).forEach(System.out::println);
  • flatMap,对每一个元素进行转换,将其变成一个Stream类型数据
List<String> words = Arrays.asList("foo", "bar");
words.stream().flatMap(x -> Arrays.asList(x, x).stream()).forEach(System.out::println);
  • filter,过滤Stream上操作
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().filter(x -> x > 2).forEach(System.out::println);
  • reduce,对Stream进行reduce操作,获得计算结果
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().reduce((x, y) -> x + y).ifPresent(System.out::println);
// 传入初始参数,参数类型与Stream中需要一致
nums.stream().reduce(100, (x, y) -> x + y);
// 传入出事参数,参数类型可以不一致
// 需要满足,combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
nums.stream().reduce("Foo", (x, y) -> x + y, (x, y) -> x + y);
  • collect,进行collect操作,构建结果
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().collect(Collectors.toList());
nums.stream().collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
  • peek,遍历Stream进行操作后,返回包含所有元素的Stream,主要用于调试
List<Integer> nums = Arrays.asList(1, 1, 2, 3, 5);
nums.stream().peek(x -> System.out.println(x * x)).peek(x -> System.out.println(x * x * x)).forEach(x -> System.out.println());

总结

Stream是Java 8中新增的重要接口,常见的函数式操作定义都可以在Stream上找寻到。弄清楚Stream提供了哪些接口能做些什么,是开始函数式编程的重要一步。这里只是简要介绍了各接口的用法,一些细节问题没有涉及到,后面再来继续的深入下去吧。