在下面的两种样式中,分配了一个Iterator对象。在迭代之前检查集合是否为空有用吗?我不知道这是否可以算是“过早优化”。希望对JVM垃圾收集器有深入了解的人能够提供洞察力。
另外,我不知道Java编译器如何处理-每个循环。我假设style B会自动转换为style A。但是..。也许还包括了一张空支票。
循环样式A
Collection<String> collection = ...
Iterator<String> iter = collection.iterator();
while (iter.hasNext()) {
String value = iter.next();
// do stuff
// maybe call iter.remove()
}循环式B型
Collection<String> collection = ...
for (String value : collection) {
// do stuff
}循环样式A(改进型)
Collection<String> collection = ...
if (!collection.isEmpty()) {
Iterator<String> iter = collection.iterator();
while (iter.hasNext()) {
String value = iter.next();
// do stuff
// maybe call iter.remove()
}
}循环样式B(改进型)
Collection<String> collection = ...
if (!collection.isEmpty()) {
for (String value : collection) {
// do stuff
}
}发布于 2019-12-04 12:24:23
是的,这肯定是一个过早的优化,如果它能有帮助的话。您的循环必须具有极其重要的性能,并且通常使用空集合调用,并且由于某种原因无法优化创建实际迭代器对象的大部分成本。
在这场完美的风暴中,这种巨大的源头丑陋可能是有道理的。但更有可能的是,您可以重新安排一些东西来帮助编译器进行更好的优化,比如保持迭代器的本地性。
迭代器对象是(通常?)函数--本地函数,因此创建起来很便宜(只需要驻留在寄存器中,不需要堆分配)。如果转义分析证明JVM是纯本地的,其他代码看不到对它的引用,请参阅https://www.beyondjava.net/escape-analysis-java获取有关JVM如何对对象进行“标量替换”的详细信息。因此,可能的节省甚至不包括内存分配。
如果这样做了,在执行某些操作之前将JIT编译成单独的检查,那么即使集合不是空的,也总是会运行额外的指令。
对最常见的情况进行优化。没有添加额外的代码来稍微加快罕见的空情况,而忽略它来加快常见的非空情况。
我认为大多数循环倾向于在非空集合上运行。小的在某些情况下是常见的,但是空的通常是罕见的。也许您有一个循环,经常或通常在空集合上运行,例如程序中很少使用的特性。那么,对这种情况进行优化是值得考虑的。(这是否是一种有用的方法,则是另一回事)
如果JIT-编译为数组上简单的指针增量循环,并且开始指针和结束指针都保存在寄存器中,那么对collection.isEmpty()的额外调用可能只是优化到循环条件中。这是最好的情况,但那额外的源噪音是无用的,你会得到什么无论如何。
您可能会争辩说,如果for (String value : collection)还没有以最有效的方式编译集合,那是编译器+JVM的错误,您不应该让源代码变得丑陋。这在某种程度上可能是正确的,尽管引入对.isEmpty()的调用并不是编译器或运行时能够做的事情,除非他们可以内联该方法以确保它确实在检查迭代器所能做的事情。但是有了JIT编译,一切都可以内联了。
TL:很可能一个好的JIT编译器并不真的为大多数简单的集合创建迭代器,所以没有什么需要保存的。
在其他情况下,除非循环通常在空集合上运行,或者(更不可能)创建迭代器,否则(为了性能)不这样做可能仍然更好。
发布于 2014-10-15 04:52:05
不,你不需要检查空的。第一次迭代将为您完成任务。
发布于 2014-10-15 04:59:10
iter.hasNext()方法将返回true/false值。如果集合中没有元素,那么迭代器只需在执行语句iter.hasNext()时返回false,循环就会优雅地终止。
https://stackoverflow.com/questions/26374334
复制相似问题