在前面章节你已经了解到了 Java8 Stream,以及通过 Java8 在 Collection 接口中新添加的 stream 方法,可以将任何集合转化为一个 Stream。
在 Java8 中,你可以通过多种方式获取一个 Stream 对象。Stream API 提供了处理数据集合(如列表、集合等)的声明性方式,它使得数据处理变得更加高效和易读。以下是一些常见的获取 Stream 的方法:
几乎所有实现了 Collection 接口的类(例如 List, Set)都提供了 stream() 方法,用于获取该集合的 Stream 表示。例如:
package com.hxstrive.jdk8.stream_api;
import java.util.*;
/**
* 集合获取流
* @author hxstrive.com
*/
public class StreamDemo8 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
System.out.println("list count=" + list.stream().count());
Set<String> set = new HashSet<>();
set.add("one");
set.add("two");
set.add("three");
System.out.println("set count=" + set.stream().count());
Map<String,String> map = new HashMap<>();
map.put("k1", "one");
map.put("k2", "two");
map.put("k3", "three");
System.out.println("map key count="+ map.keySet().stream().count());
System.out.println("map value count="+ map.values().stream().count());
}
//输出结果:
//list count=3
//set count=3
//map key count=3
//map value count=3
}Java8 的 Arrays 类提供了静态方法 stream(),用于将数组转换为 Stream。例如:
package com.hxstrive.jdk8.stream_api;
import java.util.*;
/**
* 从数组获取 Stream
* @author hxstrive.com
*/
public class StreamDemo9 {
public static void main(String[] args) {
Integer[] array = {1, 2, 3, 4, 5};
Arrays.stream(array).forEach(e -> {
System.out.println("element="+ e);
});
}
//输出结果:
//element=1
//element=2
//element=3
//element=4
//element=5
}对于原始数据类型的数组,如 int[], long[], double[] 等,Arrays 类提供了特定的静态方法,如 stream(int[] array) 来获取 Stream。例如:
package com.hxstrive.jdk8.stream_api;
import java.util.Arrays;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
/**
* 从数组获取 Stream
* @author hxstrive.com
*/
public class StreamDemo10 {
public static void main(String[] args) {
int[] primitiveArray = {1, 2, 3, 4, 5};
// 从原始数据类型数组获取 IntStream
IntStream intStream = Arrays.stream(primitiveArray);
System.out.println("intSteam count=" + intStream.count());
// 从原始数据类型数组获取 DoubleStream
double[] primitiveArray2 = {1.1d, 2.2d, 3.3d, 4.4d};
DoubleStream doubleStream = Arrays.stream(primitiveArray2);
System.out.println("doubleStream count="+ doubleStream.count());
}
//输出结果:
//intSteam count=5
//doubleStream count=4
}Arrays 中提供的 stream() 重载方法如下:

注意:使用 Arrays.stream(array, int, int) 方法将数组的一部分转化为 Stream。
在 java8 中,你可以将字符串转换为字符流。例如:
package com.hxstrive.jdk8.stream_api;
import java.util.stream.Stream;
/**
* 从字符串获取 Stream
* @author hxstrive.com
*/
public class StreamDemo11 {
public static void main(String[] args) {
String str = "hello";
// 从字符串获取字符 Stream
Stream<Character> charStream = str.chars().mapToObj(c -> (char) c);
System.out.println("charStream count=" + charStream.count());
}
//输出结果:
//charStream count=5
}使用 Stream.generate(Supplier s) 方法可以创建一个无限序列的 Stream,其中每个元素都是由提供的 Supplier 生成的。例如:
package com.hxstrive.jdk8.stream_api;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 创建无限 Stream
* @author hxstrive.com
*/
public class StreamDemo12 {
public static void main(String[] args) {
// 无限生成随机数的 Stream
Stream<Double> randomStream = Stream.generate(Math::random);
// 从无限 Stream 中获取前 10 个随机数
List<Double> list = randomStream.limit(5).collect(Collectors.toList());
list.forEach(System.out::println);
}
//输出结果:
//0.26593643769924824
//0.4294022945311554
//0.03595293972407909
//0.8079880663032635
//0.14954452516128613
}如果你要创建一个形如 0123... 的无限序列,你可以使用 iterate() 方法。该方法接受一个“种子(seed)”值和一个函数(从技术上讲,是一个 UnaryOperator<T> 接口的对象)作为参数,并且会对之前的值重复应用该函数。例如:
Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));序列中的第一个元素是种子 BigInteger.ZERO,第二个值是 f(seed),或者 1 (一个大整数);下一个元素是 f(f(seed)) 或者 2,以此类推。
使用 Stream.empty() 方法可以创建一个不包含任何元素的空 Stream。例如:
// 创建一个空的 Stream
Stream<Object> emptyStream = Stream.empty();使用 Stream.of(T t) 方法可以创建一个只包含一个元素的 Stream。例如:
package com.hxstrive.jdk8.stream_api;
import java.util.stream.Stream;
/**
* 创建无限 Stream
* @author hxstrive.com
*/
public class StreamDemo13 {
public static void main(String[] args) {
// 创建一个空的 Stream
Stream<Object> emptyStream = Stream.empty();
System.out.println("emptyStream count="+ emptyStream.count());
// 创建一个只包含一个元素的 Stream
Stream<String> singleElementStream = Stream.of("single");
System.out.println("singleElementStream count=" + singleElementStream.count());
}
//输出结果:
//emptyStream count=0
//singleElementStream count=1
}虽然这不是直接从 Java 集合或数组获取 Stream 的方法,但你可以使用 Java NIO 或第三方库(如 Apache Commons IO)从文件或输入流中读取数据并转换为 Stream。这通常涉及读取数据到缓冲区,然后将缓冲区内容转换为流。
示例:读取一个 demo.txt 文本文件,然后转换成流:
(1)假如 demo.txt 内容如下:
THE CUCKOO
布谷鸟
In April,Come he will,
四月里,它就来了,
In May,Sing all day,
五月里,整天吟唱多逍遥,
In June,Change his tune,
六月里,它在改变曲调,
In July,Prepare to fly,
七月里,准备飞翔,
In August,Go he must!
八月里,它就得离去了!(2)对应的 java 代码如下:
package com.hxstrive.jdk8.stream_api;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 从文件或输入流获取 Stream
* @author hxstrive.com
*/
public class StreamDemo13 {
public static void main(String[] args) {
String filePath = "E:\\demo.txt";
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
List<String> collectedLines = lines.collect(Collectors.toList());
collectedLines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
//输出结果:
//THE CUCKOO
//布谷鸟
//In April,Come he will,
//四月里,它就来了,
//In May,Sing all day,
//五月里,整天吟唱多逍遥,
//In June,Change his tune,
//六月里,它在改变曲调,
//In July,Prepare to fly,
//七月里,准备飞翔,
//In August,Go he must!
//八月里,它就得离去了!
}上面示例中,使用 Files.lines 方法读取文件的所有行,并将结果存储在一个 Stream<String> 中。然后,使用 try-with-resources 语句实现自动关闭流,这是处理 I/O 资源的好方法,因为它确保在不再需要流时自动关闭它。
最后,使用 collect 方法将流中的行收集到一个列表中,并且使用 forEach 方法遍历列表并打印每一行。
注意:Files.lines 默认使用 UTF-8 编码来读取文件。如果你的文件使用其他编码,你可以传递一个 Charset 参数给 Files.lines 方法来指定编码。例如:
Files.lines(Paths.get(filePath), StandardCharsets.ISO_8859_1)。此外,还要注意处理可能出现的 IOException,这是文件操作中常见的异常。
注意:在使用 Stream 时,要确保在操作完成后关闭它,尤其是在处理无限流或来自外部资源的流时。然而,对于从集合或数组获取的 Stream,这通常不是必需的,因为这些 Stream 是与底层数据源紧密绑定的,并且会在垃圾收集时自动清理。
正则表达式的 Pattern 类添加了一个 splitAsStream 的方法,能够按照正则表达式对 CharSequence 对象进行分隔。你可以使用如下代码对一个字符串按照空格进行分隔:
package com.hxstrive.jdk8.stream_api;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* 正则表达式获取 Stream
* @author hxstrive.com
*/
public class StreamDemo14 {
public static void main(String[] args) {
String str = "one two three";
Stream<String> stream = Pattern.compile("\\s+").splitAsStream(str);
stream.forEach(System.out::println);
}
//输出结果:
//one
//two
//three
}Java8 为了更好的使用 Stream,还提供了更多获取流的方法,读者自行尝试。