集合优化了对象的存储,而流(Streams)则是关于一组组对象的处理。
What
- 声明式编程(Declarative programming)
是一种编程风格——它声明了要做什么,而不是指明(每一步)如何做。相较于命令式编程(Imperative programming)的形式(指明每一步如何做)会更易理解。函数式编程和流式编程都属于声明式编程。
- 流的优势
- 流可以在不曾使用赋值或可变数据的情况下,对有状态的系统建模,这非常有用。(简单理解就是在解决同样问题时,流可以减少变量的数量,即降低问题的复杂度);
- 流使用内部迭代,产生的代码可读性更强。而且能更简单的使用多核处理器,通过放弃对迭代过程的控制,可以把控制权交给并行化机制;
- 流是懒加载的。这代表着它只在绝对必要时才计算。你可以将流看作“延迟列表”。由于计算延迟,流使我们能够表示非常大(甚至无限)的序列,而不需要考虑内存问题。
- 说明
- Stream自己不会存储元素;
- Stream 不会改变源对象,相反,它们会返回一个持有结果的新Stream;
- Stream 是懒加载的。
- Optional类
主要用于防止空指针异常,在Stream使用归约等操作后会返回Optional对象。
常用方法如下:
创建Optional类对象的方法 | |
---|---|
Optional.of(T t) | 创建一个Optional实例,t必须为非空 |
Optional.empty() | 创建一个空的Optional实例 |
Optional.ofNullable(T t) | t可以为null |
判断Optional容器中是否包含对象 | |
boolean isPresent() | 判断是否包含对象 |
void ifPresent(Consumer c) | 如果有值就将其作为参数传递给Consumer接口实例执行 |
获取Optional容器的对象 | |
T get() | 返回被包装对象,无则抛异常 |
T orElse(T other) | 返回被包装对象,无则返回指定的other对象 |
T orElseGet(Supplier other) | 返回被包装对象,无则返回Supplier实例对象 |
T orElseThrow(Supplier e) | 返回被包装对象,无则抛出Supplier实例提供的异常 |
How
流(steam)由 创建 —> 中间操作 —>终止操作 这三部分组成 细节如下:
创建(source)
- 方式一:Stream自身的静态方法
通过Stream类中的静态方法of(…)
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
- 方式二:通过集合
Collection接口提供的stream()或parallelStream()
//串行流
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
//并行流
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
- 方式三:通过数组
Arrays类的静态方法stream(…)
public static <T> Stream<T> stream(T[] array) {
return stream(array, 0, array.length);
}
方式四:创建无限流
生成器创建无限流 Stream.generate
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
迭代器创建无限流 Stream.iterate
Stream.iterate() 产生的流的第一个元素是种子(iterate方法的第一个参数),然后将种子传递给方法(iterate方法的第二个参数)。方法运行的结果被添加到流(作为流的下一个元素),并被存储起来,作为下次调用 iterate()方法时的第一个参数,以此类推。
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
操作(intermedia)
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。而在终止操作时一次性全部处理,称为“惰性求值”。
筛选与切片 | 方法 | 描述 | | —- | —- | | filter(Predicate p) | 接收Lambda,从流中过滤掉不符合的元素 | | distinct() | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素 | | limit(long maxSize) | 截断流,返回流中前n个元素的流 | | skip(long n) | 返回一个去掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
映射 | 方法 | 描述 | | —- | —- | | map(Function f) | 接收一个函数型接口,该函数会被应用到每个元素上,并将其映射成为一个新的元素 | | flatMap(Function f) | 接收一个函数型接口,将流中的每个值都换成另一个流,然后把所有流连接成一个流 | | peek(Consumer c) | peek 操作 一般用于不想改变流中元素本身的类型或者只想元素的内部状态时 |
- 排序 | 方法 | 描述 | | —- | —- | | sorted() | 产生一个新流,自然排序 | | sorted(Comparator c) | 产生一个新流,定制排序 |
终止(Terminal)
终端操作(Terminal Operations)将会获取流的最终结果,至此我们无法再继续往后传递流。因此,它总是我们在流管道中所做的最后一件事。
- 查找与匹配 | 方法 | 描述 | | —- | —- | | allMatch(Predicate p) | 检查是否匹配所有元素 | | anyMatch(Predicate p) | 检查是否至少匹配一个元素 | | noneMatch(Predicate p) | 检查是否没有匹配所有元素 | | findFirst() | 返回第一个元素 | | findAny() | 返回当前流中的任意元素 | | count() | 返回流中元素个数 | | max(Comparator c) | 返回流中最大值 | | min(Comparator c) | 返回流中最小值 |
- 归约
在大数据应用中常用map和reduce的连接,称为map-reduce模式,即,根据指定的计算模型将Stream中的值计算得到一个最终结果;
方法 | 描述 |
---|---|
reduce(T identity, BinaryOperator b) | 将流中元素反复结合起来,得到一个值T |
reduce(BinaryOperator b) | 将流中元素反复结合起来,得到一个值 Optional |
- 收集 | 方法 | 描述 | | —- | —- | | collect(Collector c) | 将流转换为其它形式。接收一个Collector接口的实现类,用于给Stream中元素做汇总的方法 |
- Collector 接口中方法的实现决定了如何对流执行收集的操作(如 收集到List、Set、Map)。另外,Collectors实用类提供类很多静态方法,可方便地创建常见收集器实例,如下:
| 方法 | 返回类型 |
| —- | —- |
| toList() | List
| | toSet() | Set | | toCollection(…) | Collection | | Collectors.groupingBy(Function classifier) | |