在 Java 编程的世界中,数据处理是一个极为重要的环节。
随着 Java 8 的推出,Java Stream 这一强大的特性为开发者们带来了全新的数据处理方式。它像是一把精巧的瑞士军刀,为我们在处理集合等数据时提供了高效、简洁且灵活的解决方案。
无论是对数据进行筛选、转换还是复杂的组合操作,Java Stream 都展现出了独特的优势。
本指南将深入探讨 Java Stream 的方方面面,包括它的概念、创建方式、常用操作方法以及并行流相关的特性和注意事项,帮助读者全面掌握这一实用的编程工具,提升在 Java 数据处理方面的能力。
Java Stream 是 Java 8 中引入的一个新的抽象层,它可以让你以一种声明式的方式处理集合(Collection)等数据。它提供了一种高效且易于理解的方式来对数据进行操作,如过滤、映射、排序等。Stream 不是数据结构,它不会存储数据,而是通过管道(pipeline)的方式对数据进行处理。
举个例子:你有一个包含整数的列表,想要获取其中所有大于 10 的偶数,使用传统的方式可能需要写一个循环来遍历列表,然后使用条件判断来筛选出符合要求的元素。而使用 Stream,你可以用更简洁的方式表达这个操作。
Stream 的优势:
代码简洁性:可以用更简洁的方式表达复杂的数据处理逻辑,减少了循环和条件判断的嵌套。
可读性:声明式的编程风格使得代码的意图更加清晰,更容易理解数据的处理流程。
可并行性:方便地利用多核处理器来提高性能,而不需要手动管理线程。
Java 集合接口List
、Set
、Map
等都提供了两个方法:
1. stream()
方法来创建一个顺序流,
2. parallelStream()
方法来创建一个并行流。
两者区别:
顺序流和并行流的区别在于它们的处理方式不同,顺序流是单线程的,而并行流是多线程的。使用的方法也有一些区别。例如:
顺序流:
执行方式:
顺序流是按照元素在集合中的顺序依次处理每个元素。在处理过程中,一个元素的操作完成后才会开始下一个元素的操作。
性能:
当处理大规模数据并且操作可以并行化时,顺序流可能会比较慢,因为它只能使用一个线程来处理所有数据。
线程安全和数据一致性:
顺序流由于是单线程进行操作,所以不存在线程安全问题。
并行流:
执行方式:
处理速度:并行流会将数据分成多个部分,同时在多个线程中对这些部分进行处理。它利用了多核处理器的优势,通过并行计算来提高处理速度。
性能:
并行流在处理大规模数据并且操作允许并行执行时,能够显著提高性能。例如,对一个包含大量整数的列表进行求和操作,使用并行流可以利用多核处理器同时计算多个部分的和,然后再合并结果,从而加快计算速度。
线程安全和数据一致性:
并行流在处理过程中会涉及多个线程同时访问数据,因此可能会出现线程安全问题。如果在并行流操作中修改共享数据,可能会导致数据不一致或错误的结果。
创建两种流的代码如下:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 创建顺序流
java.util.stream.Stream<Integer> stream = numbers.stream();
// 创建并行流
java.util.stream.Stream<Integer> parallelStream = numbers.parallelStream();
}
}
可以使用Arrays.stream()
方法从数组创建一个流
代码如下:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
java.util.stream.Stream<int[]> stream = Arrays.stream(numbers);
}
}
可以将多个元素作为参数传递给Stream.of()
方法来创建一个流。
代码如下:
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream<String> stream = Stream.of("apple", "banana", "cherry");
}
}
用于筛选出符合特定条件的元素。它接受一个Predicate
(一个返回boolean
值的函数式接口)作为参数。
代码示例:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
Stream<Integer> filteredStream = numbers.stream().filter(n -> n % 2 == 0);
// 过滤后的流只包含偶数
filteredStream.forEach(System.out::println);
}
}
用于将流中的每个元素按照指定的函数进行转换。它接受一个Function
(一个接受一个参数并返回一个结果的函数式接口)作为参数。
代码示例:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
Stream<String> mappedStream = numbers.stream().map(n -> "Number: " + n);
mappedStream.forEach(System.out::println);
}
}
distinct
)distinct
操作会去除流中重复的元素。它是通过比较元素的equals
方法来判断元素是否相同。
所以要是对对象进行去重操作的话,要想达到去重效果,对象内部必须重写equals等方法。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(1);
numbers.add(3);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
for (Integer number : distinctNumbers) {
System.out.println(number);
}
}
}
用于对流中的元素进行排序。如果流中的元素是自定义类,需要实现Comparable
接口或者提供一个Comparator
。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(1);
numbers.add(2);
Stream<Integer> sortedStream = numbers.stream().sorted();
sortedStream.forEach(System.out::println);
}
}
用于遍历流中的每个元素,并对每个元素执行给定的操作。它接受一个Consumer
(一个接受一个参数但不返回结果的函数式接口)作为参数。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.stream().forEach(System.out::println);
}
}
用于将流中的元素收集到一个集合或者其他数据结构中。它接受一个Collector
作为参数。Collectors
类提供了许多用于创建收集器的静态方法。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumbers);
}
}
用于返回流中元素的数量
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
long count = numbers.stream().filter(n -> n % 2 == 0).count();
System.out.println(count);
}
}
并行流可以利用多核处理器的优势来提高性能。当你调用集合的parallelStream()
方法或者对流调用parallel()
方法时,就会创建一个并行流。但要考虑数据安全问题。
注意:
并不是所有的操作在并行流中都能获得性能提升。对于一些简单的操作或者数据量较小的情况,并行流可能会因为线程管理等开销而比顺序流更慢。
代码示例:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
numbers.add(i);
}
long startTime = System.currentTimeMillis();
long sumSequential = numbers.stream().reduce(0, (a, b) -> a + b);
long endTimeSequential = System.currentTimeMillis();
System.out.println("Sequential sum took: " + (endTimeSequential - startTime) + "ms");
startTime = System.currentTimeMillis();
long sumParallel = numbers.parallelStream().reduce(0, (a, b) -> a + b);
long endTimeParallel = System.currentTimeMillis();
System.out.println("Parallel sum took: " + (endTimeParallel - startTime) + "ms");
}
}
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- ovod.cn 版权所有 湘ICP备2023023988号-4
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务