在前面章节你已经了解到了 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,还提供了更多获取流的方法,读者自行尝试。