为什么list.size()>0
比Java中的list.isEmpty()
慢?换句话说,为什么isEmpty()
优于size()>0
?
当我查看ArrayList
中的实现时,看起来速度应该是相同的:
ArrayList.size()
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
ArrayList.isEmpty()
/**
* Returns <tt>true</tt> if this list contains no elements.
*
* @return <tt>true</tt> if this list contains no elements
*/
public boolean isEmpty() {
return size == 0;
}
如果我们只是编写一个简单的程序来节省两种方法的时间,那么size()
在所有情况下都需要更多isEmpty()
,为什么会这样呢?
这是我的TestCode;
import java.util.List;
import java.util.Vector;
public class Main {
public static void main(String[] args) {
List l=new Vector();
int i=0;
for(i=0;i<10000;i++){
l.add(new Integer(i).toString());
}
System.out.println(i);
Long sTime=System.nanoTime();
l.size();
Long eTime=System.nanoTime();
l.isEmpty();
Long eeTime=System.nanoTime();
System.out.println(eTime-sTime);
System.out.println(eeTime-eTime);
}
}
在所有情况下都eTime-sTime>eeTime-eTime
。为什么呢?
答案 0 :(得分:76)
对于 ArrayList ,是的 - 你是正确的,操作需要(大致)同时。
对于列表的其他实现 - 例如天真链接列表* - 计算大小可能需要很长时间,而实际上只关心它是否大于零。
因此,如果您完全知道该列表是ArrayList
的实现并且永远不会改变那么它并不重要;但是:
size() == 0
仍然不比isEmpty()
更快,所以没有令人信服的理由使用前者。isEmpty
更明确地定义了您实际关心和正在测试的内容,因此使您的代码更易于理解。(另外,我修改了问题标题中NULL的用法;问题本身和这些操作与任何对象引用是否为空无关。)
*我最初在这里编写LinkedList
,隐含地引用了java.util.LinkedList,尽管该特定实现确实显式地存储了它的长度,这使得size()成为O(1)操作。更天真的链表操作可能不会这样做,并且在更一般的意义上,对List的实现没有效率保证。
答案 1 :(得分:53)
您的测试代码存在缺陷。
只需颠倒顺序,即先调用isEmpty,然后调整尺寸&gt; 0秒,你将得到相反的结果。这是由于类加载,缓存等。
答案 2 :(得分:15)
对不起,但你的基准是有缺陷的。请查看Java theory and practice: Anatomy of a flawed microbenchmark以获取有关如何处理基准测试的一般说明。
更新:要获得适当的基准,您应该查看Japex。
答案 3 :(得分:5)
你说:
所有情况下
eTime-sTime>eeTime-eTime
为什么?
首先,这可能是因为您的测试代码。您无法同时测试调用l.size()和l.isEmpty()的速度,因为它们都查询相同的值。很可能调用l.size()已将列表的大小加载到cpu缓存中,因此调用l.isEmpty()的速度要快得多。
您可以尝试在两个单独的程序中调用l.size()几百万次和l.isEmpty()几百万次但理论上编译器可以优化掉所有这些打电话,因为你实际上没有对结果做任何事情。
在任何情况下,两者之间的性能差异都可以忽略不计,尤其是在进行比较后,您需要做的是查看列表是否为空(l.size() == 0
)。生成的代码很可能看起来几乎完全相似。正如其他一些海报所指出的那样,在这种情况下,你想要优化可读性,而不是速度。
编辑:我自己测试过。这几乎是一个折腾。在size()
上使用的isEmpty()
和Vector
在长时间运行中给出了不同的结果,并没有始终如一地击败对方。在ArrayList
size()
上运行似乎更快,但不是很多。这很可能是因为对Vector
的访问是同步的,因此在尝试对这些方法进行基准访问时,您真正看到的是同步开销,这可能非常敏感。
这里需要考虑的是,当您尝试优化方法调用时,执行时间会有几纳秒的差异,那么您做错了。首先了解基础知识,例如使用Long
来long
使用{{1}}。
答案 4 :(得分:4)
.size()必须查看整个列表,而.isEmpty()可以在第一个列表中停止。
显然依赖于实现,但如前所述,如果您不需要知道实际大小,为什么还要计算所有元素呢?
答案 5 :(得分:2)
鉴于这两种实现,速度应该相同,这是真的。
但到目前为止,这些并不是这些方法的唯一可能实现方式。例如,原始链接列表(不单独存储大小的列表)可以比isEmpty()
调用快得多size()
。
更重要的是:isEmpty()
完全描述了您的意图,而size()==0
则不必要地复杂(当然不是非常复杂,但应该避免任何不必要的复杂性。)
答案 6 :(得分:1)
计算链接列表中的项目可能会非常慢。
答案 7 :(得分:1)
根据PMD(基于静态规则集的Java源代码分析器),isEmpty()是首选。 您可以在此处找到PMD规则集。搜索“UseCollectionIsEmpty”规则。
http://pmd.sourceforge.net/rules/design.html
根据我的说法,它也有助于保持整个源代码的一致性,而不是一半的人使用isEmpty(),其余的使用size()== 0。
答案 8 :(得分:0)
通常不可能说哪个更快,因为它取决于您正在使用的接口List
的实现。
假设我们在谈论ArrayList
。查找ArrayList
的源代码,您可以在JDK安装目录的 src.zip 文件中找到它。方法isEmpty
和size
的源代码如下所示(适用于Windows的Sun JDK 1.6更新16):
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
您可以很容易地看到表达式isEmpty()
和size() == 0
都会归结为完全相同的语句,因此肯定不会比另一个更快。
如果您对接口List
的其他实现如何工作感兴趣,请自行查找源代码并查找。