在开始迭代之前检查Java集合是否为空是否有用?

时间:2014-10-15 04:08:18

标签: java collections garbage-collection iteration micro-optimization

在下面的两种样式中,分配了Iterator个对象。在迭代之前检查集合是否为空是否有用?我不知道这是否属于“过早优化”。希望对JVM垃圾收集器有深入了解的人可以提供洞察力。

另外,我不知道Java编译器如何处理每个循环。我假设样式 B 会自动转换为样式 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
    }
}

4 个答案:

答案 0 :(得分:1)

是的,如果可以的话,这肯定会是过早的优化。您的循环必须对性能至关重要,并且通常由于空集合而调用,出于某种原因无法优化创建实际迭代器对象的大部分成本。

在那场完美的风暴中,这种巨大的来源丑陋可能是有道理的。但是您更有可能重新安排一些内容来帮助编译器更好地进行优化,例如将迭代器保持在本地。


迭代器对象(通常是?)是函数局部的,因此创建起来很便宜(可以只存在于寄存器中,不需要堆分配)。有关如何操作的一些详细信息,请参见https://www.beyondjava.net/escape-analysis-java如果转义分析证明JVM纯粹是本地的,并且其他代码看不到对该对象的引用,则JVM会对对象进行“标量替换”。因此,可能的节省甚至不包括内存分配。

如果这样做是在做某件事之前将JIT编译为单独的检查,那么即使该集合不为空,它也总是运行额外的指令。

针对最常见的情况进行优化。与其添加额外的代码以稍微加快罕见的空情况,不如将其删除以加快常见的非空情况。

我认为大多数循环倾向于在非空集合上运行。在某些情况下,较小的情况很常见,但通常很少见。也许您有一个经常或通常在空集合上运行的循环,例如程序中很少使用的功能。然后值得考虑针对这种情况进行优化。 (这是否是有用的解决方法,这是另一回事)

如果对J collection.isEmpty()进行额外的调用,无论它是否JIT编译为数组上的简单指针递增循环,且起始和结束指针均保存在寄存器中,则无论如何它都可能会优化循环条件。那是最好的情况,但是多余的源噪声就没用了,反正你会得到什么。


您可能会争辩说,如果for (String value : collection)尚未编译成循环访问集合的最有效方法,那是编译器+ JVM的错,并且您不必为此而使源丑陋。这一点可能是正确的,尽管引入对.isEmpty()的调用是编译器或运行时无法执行的,除非他们可以内联该方法以查看它是否确实在检查迭代器将执行的操作。但是通过JIT编译,所有内容都可以内联。


TL:DR:一个好的JIT编译器可能实际上并没有花费任何真正的工作来为大多数简单的集合创建一个迭代器,并且没有任何保存的对象。

在其他情况下,最好不要这样做(对于性能而言),除非您的循环通常在空集合上运行,或者(甚至更不可能)创建迭代器在某种程度上非常昂贵。 >

答案 1 :(得分:0)

不,你不必检查是否为空。第一次迭代会为你做到这一点。

答案 2 :(得分:0)

iter.hasNext()方法将返回true / false值。如果集合中没有元素,则迭代器只在执行语句iter.hasNext()时返回false,并且循环将正常终止。

答案 3 :(得分:0)

没有必要检查集合是否为空。如果使用for循环迭代或使用带迭代器的while循环,如果集合为空,它将不会进入迭代。

但是在迭代集合时,应该检查集合是否为空。如果集合为null并且您尝试使用循环或迭代器进行迭代,则可能抛出NullPointerException

您无需检查集合是否为空。