[英]Why is list.size()>0 slower than list.isEmpty() in Java?
为什么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;
}
如果我们只是编写一个简单的程序来获取这两种方法所花费的时间,那么 case size()
isEmpty()
,为什么会这样呢?
这是我的测试代码;
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
在所有情况下。 为什么?
对于ArrayList
,是的——你是正确的,操作需要(大致)相同的时间。
对于List
的其他实现——例如,一个简单的链表*——计算大小可能需要很长时间,而您实际上只关心它是否大于零。
因此,如果您绝对知道列表是ArrayList
的实现并且永远不会改变,那么这并不重要; 但:
size() == 0
仍然不比isEmpty()
快,因此没有令人信服的理由使用前者。isEmpty()
更清晰地定义了您真正关心和正在测试的内容,因此使您的代码更容易理解。 * 我最初在这里编写 LinkedList ,隐式引用java.util.LinkedList
,尽管该特定实现确实显式存储了它的大小,使size()
在这里成为 O(1) 操作。 一个简单的链表操作可能不会这样做,并且在更一般的意义上, List
的实现没有效率保证。
您的测试代码有缺陷。
只需颠倒顺序,即首先调用 isEmpty 和 size > 0 秒,您将得到相反的结果。 这是由于类加载、缓存等造成的。
.size() 必须查看整个列表,而 .isEmpty() 可以在第一个列表中停止。
显然依赖于实现,但正如之前所说,如果您不需要知道实际大小,为什么还要计算所有元素呢?
你说:
这里
eTime-sTime>eeTime-eTime
在所有情况下为什么?
首先,这可能是因为您的测试代码。 您不能同时测试调用 l.size() 和 l.isEmpty() 的速度,因为它们都查询相同的值。 很可能调用 l.size() 已将列表的大小加载到 cpu 缓存中,因此调用 l.isEmpty() 会快得多。
你可以尝试在两个单独的程序中调用 l.size() 几百万次和 l.isEmpty() 几百万次,但理论上编译器可以优化所有这些调用,因为你实际上并没有做任何事情结果。
无论如何,两者之间的性能差异可以忽略不计,尤其是在进行比较后,您需要查看列表是否为空( l.size() == 0
)。 生成的代码很可能看起来几乎完全相似。 正如其他一些海报所指出的,在这种情况下,您希望优化可读性,而不是速度。
编辑:我自己测试过。 这几乎是一个折腾。 Vector
上使用的size()
和isEmpty()
在长期运行中给出了不同的结果,两者都没有始终如一地击败另一个。 在ArrayList
上运行时, size()
似乎更快,但幅度不大。 这很可能是由于对Vector
的访问是同步的,因此在尝试对这些方法的访问进行基准测试时,您真正看到的是同步开销,这可能非常敏感。
这里要注意的是,当您尝试优化执行时间相差几纳秒的方法调用时,那么您做错了。 首先掌握基础知识,例如在应该使用long
的地方使用Long
。
对链表中的项目进行计数可能会非常慢。
鉴于这两种实现,速度应该是相同的,这是真的。
但到目前为止,这些并不是这些方法的唯一可能实现。 例如,原始链表(不单独存储大小的链表)可以比size()
调用更快地回答isEmpty()
。
更重要的是: isEmpty()
准确地描述了您的意图,而size()==0
是不必要的复杂(当然不是非常复杂,但应该避免任何不必要的复杂性)。
根据 PMD(基于静态规则集的 Java 源代码分析器),isEmpty() 是首选。 您可以在此处找到 PMD 规则集。 搜索“UseCollectionIsEmpty”规则。
http://pmd.sourceforge.net/rules/design.html
据我说,它还有助于保持整个源代码的一致性,而不是一半的人使用 isEmpty() 而其余的人使用 size()==0。
一般不可能说哪个更快,因为这取决于您使用的接口List
的实现。
假设我们正在谈论ArrayList
。 查找ArrayList
的源代码,您可以在 JDK 安装目录的src.zip文件中找到它。 方法isEmpty
和size
的源代码如下所示(Sun JDK 1.6 update 16 for Windows):
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
您可以很容易地看到isEmpty()
和size() == 0
这两个表达式都将归结为完全相同的语句,因此一个肯定不会比另一个快。
如果您对它在接口List
的其他实现中的工作方式感兴趣,请自行查找源代码并找出答案。
list.size() 将通过遍历一个又一个索引来计算元素的数量。 list.isEmpty() 将检查第一个元素并确定它不为空并立即给出结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.