- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
IDEA 建议替换,例如,这个:
for (Point2D vertex : graph.vertexSet()) {
union.addVertex(vertex);
}
用这个:
graph.vertexSet().forEach(union::addVertex);
这个新版本肯定更具可读性。但是,在某些情况下,我最好坚持用于可迭代对象的良好旧语言构造,而不是使用新的 foreach
方法?
例如,如果我理解正确的话,方法引用机制意味着构造一个匿名的 Consumer
对象,否则(使用 for
语言构造)将不会被构造。这会成为某些操作的性能瓶颈吗?
所以我写了这个不是很详尽的基准:
package org.sample;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole;
import org.tendiwa.geometry.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class LanguageConstructVsForeach {
private static final int NUMBER_OF_POINTS = 10000;
private static final List<Point2D> points = IntStream
.range(0, NUMBER_OF_POINTS)
.mapToObj(i -> new Point2D(i, i * 2))
.collect(Collectors.toList());
@Benchmark
@Threads(1)
@Fork(3)
public void languageConstructToBlackhole(Blackhole bh) {
for (Point2D point : points) {
bh.consume(point);
}
}
@Benchmark
@Threads(1)
@Fork(3)
public void foreachToBlackhole(Blackhole bh) {
points.forEach(bh::consume);
}
@Benchmark
@Threads(1)
@Fork(3)
public List<Point2D> languageConstructToList(Blackhole bh) {
List<Point2D> list = new ArrayList<>(NUMBER_OF_POINTS);
for (Point2D point : points) {
list.add(point);
}
return list;
}
@Benchmark
@Threads(1)
@Fork(3)
public List<Point2D> foreachToList(Blackhole bh) {
List<Point2D> list = new ArrayList<>(NUMBER_OF_POINTS);
points.forEach(list::add);
return list;
}
}
并得到:
Benchmark Mode Samples Score Error Units
o.s.LanguageConstructVsForeach.foreachToBlackhole thrpt 60 33693.834 ± 894.138 ops/s
o.s.LanguageConstructVsForeach.foreachToList thrpt 60 7753.941 ± 239.081 ops/s
o.s.LanguageConstructVsForeach.languageConstructToBlackhole thrpt 60 16043.548 ± 644.432 ops/s
o.s.LanguageConstructVsForeach.languageConstructToList thrpt 60 6499.527 ± 202.589 ops/s
为什么 foreach
在这两种情况下都更有效率:当我几乎什么都不做时,当我做一些实际工作时? foreach
不就是简单封装了Iterator
吗?这个基准是否正确?如果是,今天是否有任何理由在 Java 8 中使用旧语言结构?
最佳答案
您正在将语言的“增强型”循环与 Iterable.forEach()
进行比较方法。基准测试并没有明显错误,而且在您深入研究实现之前,结果可能看起来令人惊讶。
请注意,points
列表是 ArrayList
的一个实例,因为它是由 Collectors.toList()
收集器创建的。
Iterable
上的增强型 for 循环从中获取一个 Iterator
,然后调用 hasNext()
和 next()
重复,直到没有更多的元素。 (这不同于数组上的增强型 for 循环,它执行算术和直接数组元素访问。)因此,当循环遍历 Iterable
时,此循环将在每次迭代中执行至少两次方法调用.
相比之下,调用 ArrayList.forEach()
在包含列表元素的数组上运行一个传统的、基于 int 的 for 循环,并在每次迭代中调用一次 lambda。这里每次迭代只有一次调用,而增强型 for 循环每次迭代调用两次。这或许可以解释为什么 ArrayList.forEach()
在这种情况下更快。
黑洞案例似乎除了运行循环之外几乎没有做任何工作,所以这些案例似乎是在测量纯粹的循环开销。这可能就是为什么 ArrayList.forEach()
在这里显示出如此大的优势。
当循环只做一点点工作(添加到目标列表)时,ArrayList.forEach()
仍然有速度优势,但差异要小得多。我怀疑如果你在循环内做更多的工作,优势会更小。这表明任一构造的循环开销都非常小。尝试在循环中使用 BlackHole.consumeCPU()
。如果两种构造之间的结果变得无法区分,我不会感到惊讶。
请注意,由于 Iterable.forEach()
最终在 ArrayList.forEach()
中有一个专门的实现,因此会出现巨大的速度优势。如果您对不同的数据结构运行 forEach()
,您可能会得到不同的结果。
我不会以此为理由用调用 Iterable.forEach()
来盲目替换所有增强型 for 循环。编写最清晰、最有意义的代码。如果您正在编写性能关键代码,请对其进行基准测试!不同的表单会有不同的性能,这取决于工作负载、遍历的数据结构等。
关于java - IntelliJ IDEA 建议用 foreach 方法替换 for 循环。我应该尽可能这样做吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26615988/
我正在尝试在Elasticsearch中返回的值中考虑地理位置的接近性。我希望近距离比某些字段(例如legal_name)重要,但比其他字段重要。 从文档看来,当前的方法是使用distance_fea
我是Elasticsearch的初学者,今天在进行“多与或”查询时遇到问题。 我有一个SQL查询,需要在Elastic中进行转换: WHERE host_id = 999 AND psh_pid =
智能指针应该/可以在函数中通过引用传递吗? 即: void foo(const std::weak_ptr& x) 最佳答案 当然你可以通过const&传递一个智能指针。 这样做也是有原因的: 如果接
我想执行与以下MYSQL查询等效的查询 SELECT http_user, http_req_method, dst dst_port count(*) as total FROM my_table
我用这两个查询进行测试 用must查询 { "size": 200, "from": 0, "query": { "bool": { "must": [ { "mat
我仍在研究 Pro Android 2 的简短服务示例(第 304 页)同样,服务示例由两个类组成:如下所示的 BackgroundService.java 和如下所示的 MainActivity.j
给定标记 like this : header really_wide_table..........................................
根据 shouldJS 上的文档网站我应该能够做到这一点: ''.should.be.empty(); ChaiJS网站没有使用 should 语法的示例,但它列出了 expect 并且上面的示例似乎
我在 Stack Overflow 上读到一些 C 函数是“过时的”或“应该避免”。你能给我一些这种功能的例子以及原因吗? 这些功能有哪些替代方案? 我们可以安全地使用它们 - 有什么好的做法吗? 最
在 C++11 中,可变参数模板允许使用任意数量的参数和省略号运算符 ... 调用函数。允许该可变参数函数对每个参数做一些事情,即使每个参数的事情不是一样的: template void dummy(
我在我从事的项目之一上将Shoulda与Test::Unit结合使用。我遇到的问题是我最近更改了此设置: class MyModel :update end 以前,我的(通过)测试看起来像这样: c
我该如何做 or使用 chai.should 进行测试? 例如就像是 total.should.equal(4).or.equal(5) 或者 total.should.equal.any(4,5)
如果您要将存储库 B 中的更改 merge 到存储库 A 中,是否应该 merge .hgtags 中的更改? 存储库 B 可能具有 A 中没有的标签 1.01、1.02、1.03。为什么要将这些 m
我正在尝试执行X AND(y OR z)的查询 我需要获得该代理为上市代理或卖方的所有已售属性(property)。 我只用 bool(boolean) 值就可以得到9324个结果。当我添加 bool
我要离开 this教程,尝试使用 Mocha、Supertest 和 Should.js 进行测试。 我有以下基本测试来通过 PUT 创建用户接受 header 中数据的端点。 describe('U
我正在尝试为 Web 应用程序编写一些 UI 测试,但有一些复杂的问题希望您能帮助我解决。 首先,该应用程序有两种模式。其中一种模式是“训练”,另一种是“现场”。在实时模式下,数据直接从我们的数据库中
我有一个规范: require 'spec_helper' # hmm... I need to include it here because if I include it inside desc
我正在尝试用这个测试我在 Rails 中的更新操作: context "on PUT to :update" do setup do @countdown = Factory(:count
我还没有找到合适的答案: onclick="..." 中是否应该转义 &(& 符号)? (或者就此而言,在每个 HTML 属性中?) 我已经尝试在 jsFiddle 和 W3C 的验证器上运行转义和非
import java.applet.*; import java.awt.*; import java.awt.event.*; public class Main extends Applet i
我是一名优秀的程序员,十分优秀!