Java Stream(流)是Java 8引入的一个强大的新特性,用于处理集合数据。它提供了一种更简洁、更灵活的方式来操作数据,可以大大简化集合数据的处理
Stream流的主要特点包括
:
不存储数据:流本身不会存储元素,数据仍存储在原始的数据源(如集合)中。
不改变源对象:对流的操作与转换,不会修改其数据源,常常会产生新的Stream堆栈或结果
链式调用:可以通过一系列的方法调用来定义对流的操作,使代码更具可读性。
惰性求值:流上的操作不会立即执行,只有在遇到终端操作时才会触发计算。
函数式编程:流操作使用了函数式编程的思想,可以通过Lambda表达式来定义操作。
并行处理:可以轻松地将流操作并行化,充分利用多核处理器的性能。
创建Stream流
在使用Java Stream流之前,首先需要创建一个流。流可以从各种数据源中创建,包括集合、数组、文件等。
1.从集合创建流
可以使用集合的stream()方法来创建一个流。例如:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
Stream<String> stream = names.stream();
2.从数组创建流
可以使用Arrays.stream()方法来从数组中创建一个流。例如:
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
3.从文件创建流
可以使用Files.lines()方法来从文件中创建一个流。
中间操作(Intermediate operations)
中间操作是对流的一系列处理步骤,这些步骤会返回一个新的流,允许链式调用。
中间操作通常用于对数据进行过滤、映射、排序等操作。
这些操作可以组合形成一个流水线。一些常见的中间操作包括:
filter(Predicate predicate):根据条件过滤元素。
map(Function<T, R> mapper):将元素映射为新的值。
sorted():对元素进行排序。
distinct():去重,去除重复的元素。
limit(long maxSize):限制流中元素的数量。
skip(long n):跳过流中的前n个元素。
例如,以下代码将对一个整数集合进行筛选、映射和排序操作:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamIntermediateOperations {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
// Filter: 过滤出偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// Map: 将数字转换成他们的平方
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// Distinct: 去除重复的数字
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// Sorted: 对数字进行排序
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
// Peek: 打印数字,不改变流
numbers.stream()
.peek(System.out::println)
.count();
}
}
终端操作(Terminal operations)
终端操作会触发实际的流处理,并返回一个结果(比如输出到控制台)。
举例来说,终端操作有:
forEach:迭代流中的每个数据元素。
collect:将流转换为其他形式,接收一个Collector(如toList,toSet,toMap等)。
reduce:将流中元素结合起来,可以实现求和、找最大值等功能。
anyMatch、allMatch和noneMatch:检查流中元素是否匹配给定的谓词。
1.collect操作
collect()方法用于将流中的元素收集到一个集合或其他数据结构中。可以使用Collectors类提供的各种工厂方法创建不同类型的集合。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> collectedNames = names.stream()
.collect(Collectors.toList());
Set<String> collectedSet = names.stream()
.collect(Collectors.toSet());
Map<String, Integer> collectedMap = names.stream()
.collect(Collectors.toMap(name -> name, String::length));
2. reduce 操作
reduce()方法用于对流中的数据按照指定的计算方式计算出一个结果返回一个值。这个方法有两个参数(identity,accumulator)。
identity是初始值,accumulator是归约函数。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println(sum);
3.forEach()操作
forEach()方法用于对流中的每个元素执行指定的操作。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.forEach(name -> System.out.println("Hello, " + name));
常用的分页功能如下示例:
List<Integer> data = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
data.add(i);
}
//使用 stream() 方法将数据源转换为 Stream 对象
Stream<Integer> stream = data.stream();
int pageSize = 10; // 每页元素数量
int pageNo = 1; // 当前页码
//分页操作。使用 skip() 方法跳过指定数量的元素,使用 limit() 方法限制返回的元素数量
stream = stream.skip((pageNo - 1) * pageSize).limit(pageSize);
//如果需要对分页结果进行排序,可以使用 sorted() 方法
stream = stream.sorted();
//使用 collect() 方法将 Stream 对象转换为其他类型的集合或数组。这里使用 toList() 方法将结果收集到一个列表中
List<Integer> result = stream.collect(Collectors.toList());
//实际使用中,可以将上述操作用流水线方式连接起来,如下
list = list.stream().skip((pageIndex - 1) * pageSize).limit(pageSize).collect(Collectors.toList())
总结
Java Stream流是一项强大的特性,可以极大地简化集合数据的处理。通过中间操作和终端操作的组合,我们可以轻松地实现各种复杂的数据处理任务。同时,流还提供了并行处理的支持,可以充分利用多核处理器的性能。
要注意的是,流是一次性的,一旦调用了终端操作,流将被消耗,不能再被复用。此外,在使用并行流时要注意线程安全的问题。