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

问题描述 投票:5回答:4

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

而且,我不知道Java编译器如何处理for-each循环。我假设样式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
    }
}
java collections garbage-collection iteration micro-optimization
4个回答
1
投票

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

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


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

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

针对最常见的情况进行优化。无需添加额外的代码来稍微加快罕见的空情况,而将其保留以加快常见的非空情况。

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

collection.isEmpty()的此额外调用无论如何都可以优化到循环条件中,如果它通过数组将JIT编译为一个简单的指针递增循环,并且起始和结束指针都保存在寄存器中。这是最好的情况,但是多余的源噪声是无用的,无论如何您都会得到。


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


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

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


0
投票
不,您不必检查是否为空。第一次迭代将为您解决问题。

0
投票
iter.hasNext()方法将返回true / false值。如果collection中没有元素,则迭代器仅在执行语句iter.hasNext()时返回false,然后循环将正常终止。

0
投票
没有必要检查集合是否为空。如果要使用for循环或将while循环与迭代器一起使用,则如果集合为空,它将不会进入迭代。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.