如何在Java中使用流stream

817 阅读3分钟

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

微博上看到的一个关于javascript的集合操作函数解释,很有意思。

[■,■,■,■].map(■→●) => [●,●,●,●]
[■,●,■,▲].filter(■→true) => [■,■]
[■,●,■,▲].find(●→true) => ●
[■,●,■,▲].findIndex(●→true) => 1
[■,●,■,▲].fill(●) => [●,●,●,●]
[■,●,■,▲].some(●→true) => true
[■,●,■,▲].every(●→true) => false 

Stream的操作分为中间操作与终止操作。

每个中间操作都通过某种方式对Stream进行转换(映射,过滤等等),将一个Stream转换成另一个Stream,其元素类型可能相同,也可能不同。

终止操作会在最后一个中间操作产生的Stream上执行一个最终计算,如打印,保存到集合中,或返回某个元素。

Stream流管道操作通常是lazy的,直到调用终止操作时才会开始计算,对于完成终止操作不需要的数据元素,将永远都不会被计算。这使得无限Stream成为可能。

无限Stream的一个样例:

/**
* 	无限Stream。
* 	该方法返回无限素数Stream
* @return
*/
static Stream<BigInteger> primes() {
    return Stream.iterate(BigInteger.ONE.add(BigInteger.ONE), BigInteger::nextProbablePrime);
}

/**
* 打印出前20个素数
*/
primes().limit(20).forEach(System.out::println);

【Effective Java】[第三版]第45条,谨慎使用Stream,滥用Stream会使程序代码难以读懂和维护。

简单来说,阅读你代码的人是否习惯使用Stream。 你在流中使用的方法名称是否很好的表达了其你的意图。 用Stream的地方都可以用迭代来实现。

Stream支持对象引用和int,long与double。不支持char。最好避免利用Stream来处理char。

/**
* Effective Java 3nd 例子(P174)
**/
"Hello World".chars().forEach(System.out::print);
//output: 721011081081113287111114108100

Stream适合以下工作:

  1. 统一转换元素的序列
  2. 过滤元素的序列
  3. 利用单个操作(如添加、连接或者计算其最小值)合并元素的顺序
  4. 将元素的序列存放到一个集合中,比如根据某些公共属性进行分组
  5. 搜索满足某些条件的元素的序列

类似文章开头的JavaScript,我们列举一下Stream中的一些操作:

// java stream
[■,●,■,▲].distinct() => [■,●,▲]
[■,●,■,▲].skip(2) => [■,▲]
[■,●,■,▲].limit(2) => [■,●]
[■,●,■,▲].count() => 4
[■,●,■,▲].findFirst().orElse(null).get() => ■
[■,■,■,■].map(■→●) => [●,●,●,●]
[[■, ■], [■, ■]].flatMap([■, ■] -> stream([■, ■])) => [■, ■, ■, ■]
[■,■,■,■].forEach(■→●) => [●,●,●,●]
[■,●,■,▲].filter(■→true) => [■,■]
[4,5,2,1].sorted() => [1,2,4,5]
[■,●,■,▲].filter(■→true) => [■,■]
[■,●,■,▲].collect(groupingBy(type)) => [[■,■], [●], [▲]]
[.......].collect(Collectors.toList()) 
// 各种聚合操作, 具体查看Collectors


// 是否有元素,而非随机找元素
[■,●,■,▲].findAny().orElse(null).get() => ■ 

// 欢迎补充


不管是lambda,还是Stream,都是为了让代码更加易读。代码最终还是写给人看的。

6月4日代码补充如下

                // 辅助初始化
		List<String> list = new ArrayList<String>();
		list.add("a");
		list.add("b");
		String[] array = new String[] {"a", "b", "c"};
		
		
		/**
		 * Stream初始化
		 */
		// 空流
		Stream<String> emptyStream = Stream.empty(); // []
		// Collection(List等), Array(数组)转为Stream 
		Stream<String> listToStream = list.stream(); // [a, b]
		Stream<String> arrayToStream = Stream.of(array); // [a, b, c]
		Stream<String> arrayToStream2 = Stream.of("a", "b", "c"); // [a, b, c]
		// 取数组的一部分,[1, 3)
		Stream<String> arrayToStream3 = Arrays.stream(array, 1, 3); // [b, c]
		// builder()创建
		Stream<String> streamBuilder = Stream.<String>builder().add("a").add("b").add("c").build(); // [a, b, c]
		// generate(func),无限顺序无序流,使用时需要指定长度,否则会一直生成知道占满内存
		Stream<String> streamGenrated = Stream.<String>generate(() -> {
			Random random = new Random();
			return "" + random.nextInt(100);
		}).limit(10); // [45, 44, 51, 60, 62, 3, 57, 48, 80, 27]
		// iterate(Element(1), Element(n) -> Element(n+1))), 有序无限连续流,指定第一元素与生成后续元素的方法
		Stream<String> streamIterate = Stream.<String>iterate("a", e -> {
			return (char) (e.charAt(e.length()-1) + 1) + "";
		}).limit(26); // [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]
		// int,long,double有各自的封装良好的流IntStream, LongStream, DoubleStream
		IntStream intStream = IntStream.rangeClosed(1, 10);// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
		LongStream longStream = LongStream.range(1, 5); // [1, 2, 3, 4]
		Random random = new Random();
		DoubleStream doubleStream = random.doubles(3); // [0.8078472890889652, 0.33210715318105766, 0.9277914692743904]
		DoubleStream doubleStream2 = DoubleStream.iterate(1l, n -> n + 1l).limit(10); //[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
		
		System.out.println(emptyStream.collect(Collectors.toList()));
		System.out.println(listToStream.collect(Collectors.toList()));
		// Stream无法复用,下面一行代码会报错:java.lang.IllegalStateException: stream has already been operated upon or closed
		// System.out.println(listToStream.collect(Collectors.toList()));
		System.out.println(arrayToStream.collect(Collectors.toList()));
		System.out.println(arrayToStream2.collect(Collectors.toList()));
		System.out.println(arrayToStream3.collect(Collectors.toList()));
		System.out.println(streamBuilder.collect(Collectors.toList()));
		System.out.println(streamGenrated.collect(Collectors.toList()));
		System.out.println(streamIterate.collect(Collectors.toList()));
		System.out.println(intStream.boxed().collect(Collectors.toList()));
		System.out.println(longStream.boxed().collect(Collectors.toList()));
		System.out.println(doubleStream.boxed().collect(Collectors.toList()));
		System.out.println(doubleStream2.boxed().collect(Collectors.toList()));