我不认为添加流只是为了通过函数式编程的花招来增加代码的可读性。
如果我在流上执行多个操作,它们是如何在内部计算的。
在应用多个映射函数的情况下,映射函数是如何计算的。一次一个元素,或者对整个流执行一个映射操作,然后执行另一个操作。
在性能方面,它与对集合执行的正常迭代有何不同。
当谈论非并行流时,元素的计算顺序是否仅取决于输入集合的类型(即列表的有序、linkedHashmap、sortedset和hashSet的无序等)。
我能否对流的内部工作有更多的了解,以便更好地决定何时不建议使用流以及何时建议使用流。(需要检查的因素 例如集合大小、序列性质等)
我知道字节码是以 .class 的形式为每个类创建的。如何为流操作内部使用的所有 lambda 函数创建字节码。
List<Integer> ee = new ArrayList<Integer>();
Function<? super Integer, ? extends Integer> f1 = x -> x * 2;
Function<? super Integer, ? extends Integer> f2 = x -> x * x;
Function<? super Integer, ? extends Integer> f3 = x -> x / 2;
ee.stream().map(f1).map(f2).map(f3).collect(Collectors.toList());
最佳答案
当前的 Stream API 实现会逐一处理元素,无需中间缓冲(除非必须这样做)。对于顺序流,只有
sorted()
操作是“完全屏障”操作,因此对于map(f1).sorted().map(f2)
来说,f1
将应用于整个流,然后对结果进行排序,并且f2
将应用于每个结果项。在其他情况下,包括相邻的map(f1).map(f2)
,f1
和f2
函数将同时应用于第一个元素,然后两者都适用于第二个元素,依此类推。可能更快或更慢,很大程度上取决于许多因素,包括您使用哪些操作、哪个集合是您的流源、您有多少个输入元素、您如何收集结果。
在当前实现中 - 是的。虽然没有具体说明。该规范要求整个流中使用的大多数函数(例如传递给
map
的函数)为 stateless ,因此如果您的程序行为取决于 lambda 执行顺序,则很可能违反了指定的约定。最重要的因素是代码清晰度。如果使用流 API 使代码更易于阅读,那么使用它可能就可以了。如果您清楚地看到您的 Stream 是性能瓶颈,您可以尝试摆脱它。然而,这种情况在实践中很少发生。我倾向于避免创建许多非常短的流,因为它们会产生垃圾,因此许多短流意味着每个处理的元素有很多垃圾。然而,这在实践中并不总是重要。
Lambda 被编译为类主体内的单独合成方法,并在运行时扩展为实现相应功能接口(interface)并调用该合成方法的匿名类。这是使用
invokedynamic
字节码指令和引导工厂完成的(请参阅标准库中的java.lang.invoke.LambdaMetafactory
类)。如果您的 lambda 没有捕获任何内容,则运行时表示将是单例。否则,每次都会创建该匿名类的一个新实例。
关于java - 串行 Steam 的性能、内部工作和执行顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58967440/