在Java 8中加入的Java Streams很快就流行起来,它是处理对象集合的一种强大方式。一个流是来自一个源的对象序列,支持链式方法以产生所需的结果。
调试Java流可能是一个挑战。在这篇文章中,我们将学习如何调试流,因为它们的元素在链式方法调用中被处理。
1.为什么流难以调试?
Java 8的流有时可能难以调试。这是因为它们需要我们插入额外的断点,并彻底分析流中的每个转换。
例如,我们有一个学生类。
public class Student {
private String name;
private String email;
private Double grade;
//getters, setters, constructor, toString method
}
我们可以创建一个学生的列表。
List<Student> students = List.of(
new Student("Alexandru","alex@gmail.com",5.6),
new Student("Emmanuela","emma@yahoo.com",7.2),
new Student("John","john@gmail.com",10.0),
new Student("Andrew","andrew",6.2),
new Student("Anna","anna@gmail.com",6.2)
);
假设,我们想按字母顺序获得所有拥有有效的电子邮件地址和合格成绩的学生。所以我们使用流API操作。
List<Student> newList = students.stream()
.filter(student -> student.getEmail().endsWith("@gmail.com"))
.filter(student -> student.getGrade() > 5.0)
.sorted(Comparator.comparing(Student::getName))
.collect(Collectors.toList());
运行该程序后,我们只得到一个学生。所以我们想调试一下流,看看它是如何过滤学生的。
2.使用*peek()*API进行调试
我们可以使用*peek()*方法对流进行调试,记录每一步的数据信息。*peek()*方法返回一个由源流的元素组成的流,并执行每个元素的客户端所要求的动作。
List<Student> newList = students.stream()
.filter(student -> student.getEmail().endsWith("@gmail.com"))
.peek(student -> System.out.println("Filtered 1 value:" + student))
.filter(student -> student.getGrade() > 5.0)
.peek(student -> System.out.println("Filtered 2 value:" + student))
.sorted(Comparator.comparing(Student::getName))
.collect(Collectors.toList());
注意程序的输出。我们可以看到,*peek()方法在每次调用filter()*方法后,清楚地打印出管道中的流元素。我们可以看到,3个学生通过了第一个过滤器,只有一个学生也通过了第二个过滤器。
Filtered 1 value:Student{name="Alexandru", email="alex@gmail.com", grade=2.6}
Filtered 1 value:Student{name="John", email="john@gmail.com", grade=10.0}
Filtered 2 value:Student{name="John", email="john@gmail.com", grade=10.0}
Filtered 1 value:Student{name="Anna", email="anna@gmail.com", grade=4.2}
3.使用IntelliJ Stream Debugger进行调试
IntelliJ Stream Debugger是一个隐藏的宝石,非常容易使用。它允许你对流进行可视化。让我们在我们的例子中使用它。
第一步,我们将在流中设置一个断点。
现在我们将在调试模式下运行该程序。当流被创建时,程序将被暂停。
现在我们将按下按钮"追踪当前流链".一个新的标签将打开,在这里我们可以看到流是如何过滤数据的。
4.总结
流可能被看作是难以调试的。但在这里,流API的特殊方法,以及我们日常使用的IDE的特殊工具,会给我们带来帮助。