在了解了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构建大致有如下几种方式,
从数组创建, 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提供了哪些接口能做些什么,是开始函数式编程的重要一步。这里只是简要介绍了各接口的用法,一些细节问题没有涉及到,后面再来继续的深入下去吧。