Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作 。
Stream API 借助于 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。
Stream 不是集合元素,它也与我们平时讲到那种 IO 流不一样。它像 Iterator,但是比 Iterator 高级。它可以单向不往复的对集合进行遍历也可以进行并行化操作。
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import Java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
//Collection 子类获取流
List<String> list = new ArrayList<>();
list.add("嗨客网");
list.add("www.haicoder.net");
Stream<String> stream1 = list.stream();
//Map 获取流
Map<String, String> map = new HashMap<>();
map.put("嗨客网", "www.haicoder.net");
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
//数组获取流
String[] array = {"你好啊", "嗨客网", "www.haicoder.net"};
Stream<String> stream = Stream.of(array);
}
}
流模型的操作很丰富,常见的 API 里面主要分为两种方法,一个是终结方法,一个是非终结方法。
终结方法:返回值不在是流本身,所以它的返回结果就不能够进行后续的链式调用,常用的终结方法有:
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator。
非终结方法:返回的值仍然是 Stream 接口自身类型的方法,它们对返回的结果还能够进行链式调用。常见方法有:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等等。
map 方法可以帮我们很好的做映射,这边的映射的意思就是可以帮我们很方便的将输入的一个流的格式转换成我们需要的输出流的格式。我们举一个例子,将集合里面的小写字符串都转换为大写。
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
List<String> list = new ArrayList<>();
list.add("haicoder");
list.add("www.haicoder.net");
System.out.println("原先的数据集合元素是:" + list);
List<String> changeStrList = list.stream().map(tmpStr -> tmpStr.toUpperCase()).collect(Collectors.toList());
System.out.println("转换后的数据结果:" + changeStrList);
}
}
运行结果:
例子里面,我们如果不使用 stream 流模式的话,需要使用 for 循环遍历,或者 iteratotr 遍历集合,然后取出每个数据之后单独处理,再赋值给一个新的集合。但是有了 stream 之后,我们发现,只需要一行代码,用 map 函数,将之前的流转换,map 生成的结果还是一个 stream 对象,所以我们使用了 .collect(Collectors.toList()) 方法,将 map 生成的结果转换成 List 结合。
filter 可以帮我们过滤掉一些我们想要的数据。它可以对原有的 stream 流进行拦截,过滤,得到一个符合条件的流。
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
List<String> list = new ArrayList<>();
list.add("嗨客网");
list.add("www.haicoder.net");
list.add("嗨客");
System.out.println("原先的数据集合元素是:" + list);
List<String> filterStrList = list.stream().filter(tmpStr -> tmpStr.contains("嗨客")).collect(Collectors.toList());
System.out.println("过滤包含嗨客的词语:" + filterStrList);
}
}
运行结果如下:
通过 filter 一行语句,我们可以将原始的 stream 流过滤出只包含 “嗨客” 的词语。它得到的也是 stream 流,所以我们使用 collect(Collectors.toList()); 将返回流转换成 List 集合。
foreach 可以对 stream 流进行遍历,它与我们之前讲解的集合遍历的效果有点相似
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.List;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
List<String> list = new ArrayList<>();
list.add("嗨客网");
list.add("www.haicoder.net");
list.add("嗨客");
System.out.println("原先的数据集合元素是:" + list);
list.stream().forEach(tmpStr -> System.out.println("foreach 遍历集合:" + tmpStr));
System.out.println("结束");
}
}
运行结果如下:
可以看到,遍历集合的元素由以前的 for 循环结构体,在这里变成了一行语句,它使用起来更加方便。但是在使用的时候需要注意,该方法不能保证遍历流中元素的时候是有序的。stream 流遍历到结束的时候,就不能够再次遍历了,你不能对同一个 stream 流进行两次遍历,所以想要第二次再遍历集合的时候,需要新生成一个 stream 流。
limit 是对流进行截取,只获取流的前几个元素。
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
List<String> list = new ArrayList<>();
list.add("嗨客网");
list.add("www.haicoder.net");
list.add("嗨客");
System.out.println("原先的数据集合元素是:" + list);
List<String> limitStrList = list.stream().limit(2).collect(Collectors.toList());
System.out.println("截取后的集合为:" + limitStrList);
}
}
运行结果如下
limit 对流进行截图后得到的结果也是一个 stream 流对象,所以我们使用 collect(Collectors.toList()) 将流对象转换成了一个 list 结合对象。
sorted 就是将集合元素进行排序,按照自己定义的规则进行排序。
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
List<String> list = new ArrayList<>();
list.add("嗨客网");
list.add("www.haicoder.net");
list.add("嗨客");
list.add("Z");
list.add("G");
list.add("A");
System.out.println("原先的数据集合元素是:" + list);
List<String> sortStrList = list.stream().sorted((tmpstr, tmp) -> tmpstr.compareTo(tmp)).collect(Collectors.toList());
System.out.println("排序后的集合为:" + sortStrList);
}
}
运行结果如下:
因为 String 里面重写了 compare 函数,所以有些同学看到我们例子的时候,就会认为 (tmpstr, tmp) -> tmpstr.compareTo(tmp) 这样的语句是多余的,没有必要的。这边这样写的原因是为了给 List 里面存放自定义对象的时候,对元素进行比较的时候做参考。
如果有两个流,想要合并成一个流,那么可以使用 concat 方法,但是前提是两个流对应的原始对象要一致。
package com.haicoder.net.strea;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.Collectors;
import Java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
System.out.println("嗨客网(www.haicoder.net)");
List<String> list = new ArrayList<>();
list.add("嗨客网");
list.add("www.haicoder.net");
list.add("嗨客");
List<String> appendList = new ArrayList<>();
appendList.add("Z");
appendList.add("G");
appendList.add("A");
System.out.println("原先的数据集合元素是:" + list);
System.out.println("追加的数据集合元素是:" + appendList);
List<String> resultStrList = Stream.concat(list.stream(), appendList.stream()).collect(Collectors.toList());
System.out.println("结果集合为:" + resultStrList);
}
}
运行结果如下
可以看到最后的结果为将两个集合合并了,只有一行简单的 stream 操作语句,提升了代码的阅读性。
stream 的流式操作,使用 lambda 表达式,让对 Java 集合操作更加方便,代码更加简洁。