Java 8 Stream 速学
Java 8 引入了便捷、高效处理一系列元素的新 API —— java.util.stream
。
创建流
一旦创建出 Stream
实例后,对该实例的修改并不会影响到源数据,故可以从一个源数据中创建出多个 Stream
实例。
创建空流
创建一个空的 Stream
实例:
Stream<String> streamEmpty = Stream.empty();
常用于避免返回 null
的情况:
public Stream<String> streamOf(List<String> list) { return list == null || list.isEmpty() ? Stream.empty() : list.stream(); }
从集合中创建流
可以从任何实现了 java.util.Collection
接口(定义了一个默认方法 stream()
)的集合类(如 List
、 Set
等)中创建 Stream
实例:
Collection<String> collection = Arrays.asList("a", "c", "b"); Stream<String> streamOfCollection = collection.stream();
从数组中创建流
1、从数组源数据中创建流:
Stream<String> stream = Stream.of("a", "c", "b");
2、从已有数组中创建流:
String[] arr = {"a", "c", "b"}; Stream<String> streamOfArrayFull = Arrays.stream(arr); // a c b Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 2); // c
Stream.builder()
当使用 builder 时,特定的类型应该在语句的右边部分额外指定,否则 build()
方法将创建 Stream<Object>
实例对象:
Stream<String> streamBuilder = Stream.<String>builder().add("a").add("b").add("c").build();
Stream.generate()
generate()
方法接受 Supplier<T>
进行元素生成。由于生成的流是无限的,开发人员应该指定所需的大小,否则 Generate()
方法将一直工作,直到内存限制:
Stream<String> streamGenerated = Stream.generate(() -> "element").limit(5);
Stream.iterate()
创建无限流的另一种方法是使用 iterate()
方法:
Stream<Integer> streamIterated = Stream.iterate(2, n -> n + 2).limit(3); // 2 4 6
原始类型流
Java 8 可以从三种基本类型( int
、 long
和 double
)中创建流。由于 Stream<T>
是一个泛型接口,无法使用原始类型作为泛型的类型参数,因此 Java 还提供了三个新的特殊接口: IntStream
、 LongStream
、 DoubleStream
。
使用新的接口可以减少不必要的自动装箱,从而提高工作效率:
IntStream intStream = IntStream.range(1, 3); LongStream longStream = LongStream.rangeClosed(1, 3);
此外,Java 8 Random
类还提供用于生成随机数原语流的各种方法。比如:
Random random = new Random(); DoubleStream doubleStream = random.doubles(3);
字符串流
字符串也可以用作创建流的源。
1、借助 String
类的 chars()
方法。 因为 JDK 中没有 CharStream
接口,所以使用 IntStream
来表示字符流:
IntStream streamOfChars = "abc".chars();
2、根据指定的正则表达式将字符串拆分为子字符串:
Stream<String> streamOfString = Pattern.compile(", ").splitAsStream("a, b, c"); // a b c
文件流
Java NIO 类文件允许通过 lines()
方法生成文本文件的 Stream<String>
。文本的每一行都成为流的一个元素:
Path path = Paths.get("D:\\test.txt"); Stream<String> streamOfStrings = Files.lines(path); Stream<String> streamWithCharset = Files.lines(path, StandardCharsets.UTF_8);
多线程流
从任何实现了 java.util.Collection
接口(定义了一个默认方法 parallelStream()
)的类中获取多线程(并行模式)流。比如:
List<String> list = new ArrayList<String>() {{ add("a"); add("c"); add("b"); }}; Stream<String> stream = list.parallelStream();
操作流
流中的操作分为两类:
- 中间操作:返回
Stream<T>
,允许链式调用 - 终端操作:返回最终结果,不可再被该流进行后续操作
注意:对流的操作只会影响该实例化的流,而不影响源数据。
遍例
替代 for
、 for-each
和 while
循环语句。比如:
List<String> list = new ArrayList<String>() {{ add("t_a"); add("t_c"); add("b"); }}; for (String item : list) { if (item.startsWith("t_")) { System.out.println(item); } }
将上面的 for-each
更改为 Java 8 Stream 代码:
list.stream().filter(item -> item.startsWith("t_")).forEach(System.out::println);
过滤
filter()
:通过指定条件来过滤流中的元素。比如:
List<String> names = new ArrayList<String>() {{ add("Jason"); add("Jason Wu"); add("Bruce"); add("Bruce Lee"); }}; names.stream().filter(element -> element.contains("Jason")).forEach(System.out::println);
映射
1、 map()
:对流中的元素应用某个特定函数,然后再将这些函数结果组装为一个新的流。比如:
String[] numbers = new String[]{"1", "3", "2"}; Stream<Integer> stream = Arrays.stream(numbers).map(Integer::parseInt);
2、 flatMap()
:从流中元素值组装出一个新的流。比如:
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class LearnTest { public static void main(String[] args) { List<User> users = new ArrayList<User>() {{ add(new User("Jason", "Java", "JS")); add(new User("Bruce", "Python", "Go")); }}; Stream<String> stream = users.stream().flatMap(user -> user.hobbies.stream()); stream.forEach(System.out::println); } static class User { private String userName; private List<String> hobbies; public User(String userName, String... hobbies) { this.userName = userName; this.hobbies = new ArrayList<>(Arrays.asList(hobbies)); } } }
匹配
anyMatch()
、 allMatch()
和 noneMatch()
方法已命名自解释。比如:
boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false
缩减操作
reduce()
:第一个参数为起始值,第二个参数为累加器函数。比如:
List<Integer> integers = Arrays.asList(1, 1, 1); Integer reduced = integers.stream().reduce(23, Integer::sum); // 26
或多线程流下:
int reduced = Arrays.asList(1, 2, 3).parallelStream().reduce(10, (a, b) -> { System.out.println("Accumulator was called: " + (a + b)); return a + b; }, (a, b) -> { System.out.println("Combiner was called: " + (a + b)); return a + b; }); System.out.println(reduced); // Accumulator was called: 12 // Accumulator was called: 13 // Combiner was called: 25 // Accumulator was called: 11 // Combiner was called: 36 // 36
收集器
collect()
:将流转换为 Collection
、 Map
或 String
。其中工具类 Collectors
提供了一些常用的解决方案。
List<String> list = new ArrayList<String>() {{ add("js"); add("edm"); }}; String str = list.stream().map(String::toUpperCase).collect(Collectors.joining(","));
关闭流
在实际开发中,我们一定要注意最后关闭流(可通过 close()
方法,或执行终端操作)。因为不及时关闭实例化的流,可能会导致内存泄漏。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论