目录
一.什么是Stream
Stream是Java8提供了一种高效且易于使用的处理数据的方式,类似于数据库查询语句,可以理解为遍历数据集的高级迭代器。要处理集合看作一种流, 在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。集合是数据,流就是处理中的数据,也是一种计算方式。
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
1.Stream的特征
Stream(流)是一个来自数据源的元素队列并支持聚合操作。
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源:可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
2.Stream的原理
https://blog.csdn.net/lcgoing/article/details/87918010
3.Stream和Collections的区别
-
无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
-
为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
-
惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
-
可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
4.Stream的操作
- 中间操作(intermediate operations):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
- 结束操作(terminal operations):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
- 中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream,仅此而已。
- 结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。
- short-circuiting操作:对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件。
常用API:
- Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered。
- Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator。
- Short-circuiting:anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit。
5.不使用Stream和使用Stream对比
不使用Stream:
package Lambda;
import java.util.*;
public class Liu {
public static final void main(String[] s) {
List<Student> students = new ArrayList<>(Arrays.asList(new Student(2, "好"),
new Student(3, "坏"),new Student(4, "好"),new Student(1, "好")));
List<Student> newStudents = new ArrayList<>();
//选择特定元素,放进newStudents里
for(Student t: students){
if(t.getType() == Student.STU_TYPE){
newStudents.add(t);
}
}
//根据idnewStudents将排序
Collections.sort(newStudents, new Comparator<Student>(){
@Override
public int compare(Student t1, Student t2){
return t2.getId().compareTo(t1.getId());
}
});
//把经过排序的集合newStudents的ids提取出来,放进studentIds里
List<Integer> studentIds = new ArrayList<>();
for(Student t: newStudents){
studentIds.add(t.getId());
}
//打印
System.out.println(studentIds);
}
/**
* 学生类
*/
static class Student {
private static final String STU_TYPE = "好";
private Integer id;
private String type;
public Student(Integer id, String type) {
this.id = id;
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
使用Stream:
package Lambda;
import java.util.*;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
public class Liu {
public static final void main(String[] s) {
List<Student> students = new ArrayList<>(Arrays.asList(new Student(2, "好"),
new Student(3, "坏"),new Student(4, "好"),new Student(1, "好")));
List<Student> newStudents = new ArrayList<>();
// 选择特定元素 -> 排序 -> 取值
List<Integer> studentIds = students.parallelStream().
filter(t -> t.getType() == Student.STU_TYPE).
sorted(comparing(Student::getType).reversed()).
map(Student::getId).
collect(toList());
//打印
System.out.println(studentIds);
}
/**
* 学生类
*/
static class Student {
private static final String STU_TYPE = "好";
private Integer id;
private String type;
public Student(Integer id, String type) {
this.id = id;
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
二.常见应用场景
1.生成流
stream() − 为集合创建串行流:
//去空字符串
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
parallelStream() − 为集合创建并行流:
//获取空字符串的数量
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
2.forEach
Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。
// 使用Stream.forEach()进行迭代
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.forEach(str -> System.out.println(str));
3.map
map 方法用于映射每个元素到对应的结果。
//获取对应的平方数
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());
4.filter
filter 方法用于通过设置的条件过滤出元素。
//获取空字符串的数量
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.stream().filter(string -> string.isEmpty()).count();
5.limit
limit 方法用于获取指定数量的流。
//输出10个随机数
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
6.sorted
sorted 方法用于对流进行排序。
//对10个随机数进行排序
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
7.Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串。
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
8.统计
一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,可以用来产生类似如下的统计结果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
学习参考:
http://www.runoob.com/java/java8-streams.html
http://www.cnblogs.com/CarpenterLee/p/6545321.html
https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/