我在 Person.java
文件中有一个 POJO
:
public class Person {
private String name;
private int age;
public Person(String n, int a) {
name = n;
age = a;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public boolean isAdult() {
return getAge() >= 18;
}
}
然后我有一个 Demo.java
文件,它创建一个人员列表并使用流来过滤和打印列表中的内容:
import java.util.*;
public class Demo {
public static void main(String[] args) {
List<Person> people = createPeople();
List<String> names = people.stream()
.filter(person -> person.isAdult())
.map(person -> person.getName())
.collect(toList());
System.out.println(names);
}
private static List<Person> createPeople() {
List<Person> people = new ArrayList<>();
people.add("John", 19);
people.add("Joe", 21);
people.add("Jill", 16);
people.add("Sylvester", 18);
people.add("Hillary", 17);
people.add("Donald", 4);
return people;
}
}
我想知道:
1> filter()
和 map()
是否在内部使用循环遍历 中的所有
Person
对象列出人
?
2> 如果是,它们是否对列表中的所有对象进行两次不同的循环(第一次迭代通过 filter()
进行,另一次通过 map()
进行)?
3> 如果再次是,如果我添加另一个map()
或filter()
方法,它会第三次再次遍历所有对象吗?
4> 如果再次是,那么它与我们传统的命令式风格编码在性能方面有何不同(事实上,在传统的命令式风格中,大多数时候我们可以在一个循环中完成所有过滤和映射。所以在性能方面,命令式编码在这种情况下会比流表现得更好。)?
PS:如果对上述任何问题的回答是No
,请添加关于事情如何运作的解释。
还有一个问题:流在内部完成的迭代和我们在命令式风格中进行的迭代有区别吗?请通过一些详细的解释进一步阐明我的知识。
最佳答案
首先,您的代码无法编译,因为 map()
返回一个 Stream
,而不是一个 List
。您必须使用终端操作结束流链,并且 filter()
和 map()
是intermediate operations .在 javadoc 中就是这么说的。在您的情况下,您需要添加 .collect(Collectors.toList())
以使其编译和运行正常。
1> Does
filter()
andmap()
internally use a loop to iterate over all thePerson
objects in theList
people?
没有。终端操作正在循环。
由于问题 2 到 4 假定答案是Yes
,因此他们没有答案。
If there is a
No
to any of the above questions, please add an explanation regarding how things work then.
阅读documentation ,或搜索网络。解释得很好。
Is there a difference in iteration done by the stream internally and the iteration we do in imperative style?
是的,例如流可以利用并行线程执行。即使是单线程,整个操作的工作方式也有所不同,尽管从逻辑上讲,在较高层次上,它们本质上是相同的。
示例
在您的代码中,添加了 collect()
调用后,等效的命令式代码为:
List<String> names = new ArrayList<>();
for (Person person : people)
if (person.isAdult())
names.add(person.getName());
为了与流逻辑的作用进行比较,首先定义传递给 filter()
和 map()
的 lambda:
Predicate<Person> filter = person -> person.isAdult();
Function<Person, String> map = person -> person.getName();
然后通过调用 Collectors.toList()
获取 Collector
,并检索它提供的对象:
Collector<String, List<String>, List<String>> collector = (Collector) Collectors.toList();
Supplier<List<String>> supplier = collector.supplier();
BiConsumer<List<String>, String> accumulator = collector.accumulator();
Function<List<String>, List<String>> finisher = collector.finisher();
现在,stream()
调用基本上提供了一个Iterator
(它实际上是一个Spliterator
)和collect
调用将迭代,因此将它们组合起来等同于 for
循环。我不会介绍 Stream
、Spliterator
和 collect()
工作原理的完整逻辑。如果您需要更多详细信息,请搜索网络。
因此,上面的命令式 for
循环变为:
List<String> list = supplier.get(); // list = new ArrayList<>()
for (Person person : people) // collect() loop using Spliterator from stream()
if (filter.test(person)) { // if (person.isAdult())
String value = map.apply(person); // value = person.getName()
accumulator.accept(list, value); // list.add(value)
}
List<String> names = finisher.apply(list); // names = list
关于java - java8 Stream的filter()和map()方法是否使用迭代?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41654583/