前面我们学习了Java 8接口变更和功能接口以及Lambda表达式。这篇文章中将学习Java 8中引入的一个主要API - Java Stream。
在学习Java Stream API示例之前,让我们看看为什么需要它。假设想迭代一个整数列表,找出大于10
的所有整数的总和。
在Java 8之前,使用方法是:
private static int sumIterator(List<Integer> list) {
Iterator<Integer> it = list.iterator();
int sum = 0;
while (it.hasNext()) {
int num = it.next();
if (num > 10) {
sum += num;
}
}
return sum;
}
上述方法存在三个主要问题:
- 只想知道整数之和,但还必须提供迭代的发生方式,这也称为外部迭代,因为客户端程序正在处理迭代列表算法。
- 该程序本质上是顺序的,无法轻易地并行执行此操作。
- 即使是一项简单的任务,也有很多代码可以完成。
为了克服上述所有缺点,引入了Java 8 Stream API。可以使用Java Stream API来实现内部迭代,这更好,因为java框架控制着迭代。
内部迭代提供了几个功能,如顺序和并行执行,基于给定条件的过滤,映射等。
大多数Java 8 Stream API方法参数都是功能接口,因此lambda表达式可以很好地与它们配合使用。让我们看看如何使用Java Streams在单行语句中编写上述逻辑。
private static int sumStream(List<Integer> list) {
return list.stream().filter(i -> i > 10).mapToInt(i -> i).sum();
}
请注意,上述程序使用java框架迭代策略,过滤和映射方法,并将提高效率。
首先,我们将研究Java 8 Stream API的核心概念,然后将通过一些示例来了解最常用的方法。
集合和Java Stream
集合是用于保存值的内存数据结构,在开始使用集合之前,应该已经填充了所有值。而Java Stream是按需计算的数据结构。
Java Stream不存储数据,它在源数据结构(集合和数组)上运行,并生成可以使用并执行特定操作的流水线数据。例如,可以从列表中创建流并根据条件对其进行过滤。
Java Stream操作使用功能接口,这使得它非常适合使用lambda表达式进行函数式编程。正如您在上面的示例中所看到的,使用lambda表达式使我们的代码可读且简短。
Java 8 Stream内部迭代原理有助于在某些流操作中实现延迟搜索。例如,可以懒惰地实现过滤,映射或重复删除,从而允许更高的性能和优化范围。
Java Streams是可以使用的,因此无法创建对流的引用以供将来使用。由于数据是按需的,因此不可能多次重复使用相同的流。
Java 8 Stream支持顺序处理和并行处理,并行处理对于实现大型集合的高性能非常有用。
所有Java Stream API接口和类都在java.util.stream
包中。由于可以在使用自动装箱的集合中使用原始数据类型(如int
,long
),并且这些操作可能会花费大量时间,因此有基本类型的特定类 - IntStream
,LongStream
和DoubleStream
。
Java 8 Stream中的Functional 接口
Java 8 Stream API方法中的一些常用 Functional
接口是:
1. Function和BiFunction:Function
表示一个函数,它接受一种类型的参数并返回另一种类型的参数。Function<T, R>
是通用形式,其中T
是函数输入的类型,R
是函数结果的类型。
对于处理基元类型,有特定的函数接口 - ToIntFunction
,ToLongFunction
,ToDoubleFunction
,ToIntBiFunction
,ToLongBiFunction
,ToDoubleBiFunction
,LongToIntFunction
,LongToDoubleFunction
,IntToLongFunction
,IntToDoubleFunction
等。
2. Predicate和BiPredicate:它代表一个谓词,用于测试流的元素。这用于过滤java流中的元素。就像Function
一样,int
,long
和double
都有原始的特定接口。
3. Consumer和BiConsumer:它表示接受单个输入参数并且不返回任何结果的操作。它可用于对java流的所有元素执行某些操作。
4. Supplier:Supplier代表一种操作,通过该操作可以在流中生成新值。
Java Stream示例
前面介绍了Java 8 Stream API的所有重要部分。使用这个新的API功能令人兴奋,让我们看看它与一些java流示例的实际应用。
创建Java Stream
可以通过多种方式从数组和集合中创建Java Stream。让我们用简单的例子来看看如何使用。
第1种. 可以使用Stream.of()
从相似类型的数据创建流。例如,可以从一组int
或Integer
对象创建Java的整数Stream。
Stream<Integer> stream = Stream.of(1,2,3,4);
第2种. 使用带有Object数组的Stream.of()
来返回Stream
。请注意,它不支持自动装箱,因此无法传递基本类型数组。
Stream<Integer> stream = Stream.of(new Integer[]{1,2,3,4});
//works fine
Stream<Integer> stream1 = Stream.of(new int[]{1,2,3,4});
//Compile time error, Type mismatch: cannot convert from Stream<int[]> to Stream<Integer>
第3种. 使用Collection stream()创建顺序流和parallelStream()
来创建并行流。
List<Integer> myList = new ArrayList<>();
for(int i=0; i<100; i++) myList.add(i);
//sequential stream
Stream<Integer> sequentialStream = myList.stream();
//parallel stream
Stream<Integer> parallelStream = myList.parallelStream();
第4种. 使用Stream.generate()
和Stream.iterate()
方法来创建Stream。
Stream<String> stream1 = Stream.generate(() -> {return "abc";});
Stream<String> stream2 = Stream.iterate("abc", (i) -> i);
第5种. 使用Arrays.stream()
和String.chars()
LongStream is = Arrays.stream(new long[]{1,2,3,4});
IntStream is2 = "abc".chars();