Stream 作为 Java8 中的一大亮点,专注于对集合(Collection)进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。
Stream 代表数据流,流中的元素可能是有限个也可能是无限个。
Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的数据的访问和有效管理(增删改),而Stream并没有提供访问和管理元素的方式,而是通过声明数据源的方式,利用可计算的操作在数据源上执行,当然BaseStream.iterator() 和 BaseStream.spliterator()操作提供了遍历元素的方法。
Stream 的类型有:IntStream、LongStream 和 DoubleStream。
常见的 Stream 创建方式有:
当然还有其它方法,比如 Arrays.stream()、StreamSupport.stream() 。
Stream.of()
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
内部通过 Arrays.stream() 进行创建。
示例:
Stream<String> streamStr = Stream.of("a","b","c");
Stream<Integer> stream = Stream.of(1,2,3,4,5);
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);
}
内部通过 StreamSupport 进行生成。
示例:
Stream.generate(()->{
return Math.random();
});
Stream.generate(new Supplier<Integer>() {
@Override
public Integer get() {
return Math.round(5);
}
});
在 JDK1.8 中针对 Collection 接口进行了改动,其中包括转换 stream 的 default 方法。
/**
* Returns a sequential {@code Stream} with this collection as its source.
*
* This method should be overridden when the {@link #spliterator()}
* method cannot return a spliterator that is {@code IMMUTABLE},
* {@code CONCURRENT}, or late-binding. (See {@link #spliterator()}
* for details.)
*
* @implSpec
* The default implementation creates a sequential {@code Stream} from the
* collection's {@code Spliterator}.
*
* @return a sequential {@code Stream} over the elements in this collection
* @since 1.8
*/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
/**
* Returns a possibly parallel {@code Stream} with this collection as its
* source. It is allowable for this method to return a sequential stream.
*
* This method should be overridden when the {@link #spliterator()}
* method cannot return a spliterator that is {@code IMMUTABLE},
* {@code CONCURRENT}, or late-binding. (See {@link #spliterator()}
* for details.)
*
* @implSpec
* The default implementation creates a parallel {@code Stream} from the
* collection's {@code Spliterator}.
*
* @return a possibly parallel {@code Stream} over the elements in this
* collection
* @since 1.8
*/
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream();
list.parallelStream();
}
在实际的开发中,一般我们通过 Collection 的 default 方法来获取集合对应的 Stream,针对该 Stream 进行处理。
Stream 的转换主要是针对 Stream 提供的方法来进行针对 Stream 的处理,比如过滤、判断、计数等。
遍历元素,Consumer 接口是输入一个参数,无返回结果。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream().forEach((x)->{
System.out.println(x);
});
}
//结果
1
2
3
4
这里的例子中都直接使用 lambda 表达式进行书写。
去重,跟 SQL 中的关键字作用相同,经过 distinct 处理的 Stream 里面不包含重复元素。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(1);
list.add(3);
list.stream().distinct().forEach(x ->{
System.out.println(x);
});
}
//结果
1
2
3
4
针对 Stream 中的元素根据指定的 predicate 函数进行过滤,新生成的 Stream 只包含过来后的元素。Predicate 接口是输入一个参数,返回一个布尔类型。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(1);
list.add(3);
list.stream().filter(x->{
if (x > 2) {
return true;
}
return false;
}).forEach(x->{
System.out.println(x);
});;
}
//结果
3
4
3
对于 Stream 中包含的元素使用给定的转换函数进行转换操作,新生成的 Stream 只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong 和 mapToDouble。这三个方法也比较好理解,比如 mapToInt 就是把原始 Stream 转换成一个新的 Stream,这个新生成的 Stream 中的元素都是 int 类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(1);
list.add(3);
list.stream().map((x)->{
if (x > 2) {
return x * 2;
}else {
return x * 3;
}
}).forEach(x->{
System.out.println(x);
});;
}
//结果
3
6
6
8
3
6
将给定的元素转换成 Stream 流进行输出。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(1);
list.add(3);
list.stream().flatMap(x ->{
return Stream.of(x * 2);
}).forEach(x->{
System.out.println(x);
});;
}
进行排序。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(1);
list.add(3);
list.stream().sorted().forEach(x->{
System.out.println(x);
});;
}
生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.stream().peek(x->{
x = x + 10;
System.out.println(x+"--");
}).forEach(x->{
System.out.println(x);
});
}
//结果
11--
1
12--
2
13--
3
对一个 Stream 进行截断操作,获取其前N个元素,如果原 Stream 中包含的元素个数小于 N,那就获取其所有的元素。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.stream().limit(2).forEach(x->{
System.out.println(x);
});
}
//结果
1
2
返回一个丢弃原 Stream 的前N个元素后剩下元素组成的新 Stream,如果原 Stream 中包含的元素个数小于 N,那么返回空 Stream。
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.stream().skip(1).forEach(x->{
System.out.println(x);
});
}
// 结果
2
3
在 Stream 中提供了 reduce 操作,reduce 操作实现从 Stream 中元素组合起来,依照特定的某种规则进行计算。比如常见的:sum、min、max、average 计算操作。reduce 操作提供三个重载函数:
在这里引入了 BinaryOperator 接口,这个接口继承 BiFunction 接口。
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
包含一个两个参数的 apply 方法。
sum 操作
public static void main(String[] args) {
Stream<Integer> list = Stream.of(1,2,3,4,5,6);
Optional<Integer> result = list.reduce((x, y) -> x + y);
System.out.println(result.get());
}
max 操作
public static void main(String[] args) {
BinaryOperator<Integer> binaryOperator = (Integer x, Integer y) -> {
if (x < y) {
return y;
} else {
return x;
}
};
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
System.out.println("max:" + stream.reduce(binaryOperator).get());
Optional<Integer> optional = stream.reduce((Integer x, Integer y) -> {
if (x < y) {
return y;
} else {
return x;
}
});
System.out.println("max:" + optional.get());
}
在上面的示例中,我们可以通过定义 BinaryOperation 来实现操作逻辑,也可以使用系统封装好的函数:Integer::max 进行处理。
min 操作
public static void main(String[] args) {
BinaryOperator<Integer> binaryOperator = (Integer x, Integer y) -> {
if (x < y) {
return x;
} else {
return y;
}
};
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
System.out.println("min:" + stream.reduce(binaryOperator).get());
Optional<Integer> optional = Stream.of(1,2,3,4,5,6).reduce((Integer x, Integer y) -> {
if (x < y) {
return x;
} else {
return y;
}
});
System.out.println("min:" + optional.get());
}
本节中介绍 Java8 中针对集合的增强操作,熟练的使用这些方法可以提升代码的简洁性。