为什么list.size()> 0比Java中的list.isEmpty()慢?

时间:2009-10-02 11:26:42

标签: java list

为什么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。为什么呢?

9 个答案:

答案 0 :(得分:76)

对于 ArrayList ,是的 - 你是正确的,操作需要(大致)同时。

对于列表的其他实现 - 例如天真链接列表* - 计算大小可能需要很长时间,而实际上只关心它是否大于零。

因此,如果您完全知道该列表是ArrayList的实现并且永远不会改变那么它并不重要;但是:

  1. 无论如何,这都是糟糕的编程习惯,将自己束缚于具体的实施。
  2. 如果事情发生变化几年后代码重组,测试将显示“它工作”但事情运行效率低于以前
  3. 即使在最好的情况下,size() == 0仍然不比isEmpty()更快,所以没有令人信服的理由使用前者。
  4. isEmpty更明确地定义了您实际关心和正在测试的内容,因此使您的代码更易于理解。
  5. (另外,我修改了问题标题中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的访问是同步的,因此在尝试对这些方法进行基准访问时,您真正看到的是同步开销,这可能非常敏感。

这里需要考虑的是,当您尝试优化方法调用时,执行时间会有几纳秒的差异,那么您做错了。首先了解基础知识,例如使用Longlong使用{{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 文件中找到它。方法isEmptysize的源代码如下所示(适用于Windows的Sun JDK 1.6更新16):

public boolean isEmpty() {
    return size == 0;
}

public int size() {
    return size;
}

您可以很容易地看到表达式isEmpty()size() == 0都会归结为完全相同的语句,因此肯定不会比另一个更快。

如果您对接口List的其他实现如何工作感兴趣,请自行查找源代码并查找。