【问题标题】:Why not always use ArrayLists in Java, instead of plain ol' arrays?为什么不总是在 Java 中使用 ArrayLists 而不是普通的 ol' 数组?
【发布时间】:2009-04-04 14:54:04
【问题描述】:

这里有个小问题:为什么不总是在 Java 中使用 ArrayLists?除了额外有用的功能外,它们显然具有与数组相同的访问速度。我理解它不能容纳原语的限制,但是通过使用包装器很容易缓解这种情况。

【问题讨论】:

标签: java


【解决方案1】:

如果您需要一组基元,那么数组可能是完成这项工作的最佳工具。拳击是一项相对昂贵的操作。对于将用作图元的图元集合(不包括地图),我几乎总是使用数组来避免重复装箱和拆箱。

不过,我很少担心数组和ArrayList 之间的性能差异。如果List 将提供更好、更清洁、更易于维护的代码,那么我将始终使用List(或CollectionSet 等,视情况而定,但您的问题是关于ArrayList),除非有有一些令人信服的理由不这样做。性能很少是令人信服的原因。

使用Collections 几乎总能得到更好的代码,部分原因是数组不能很好地与泛型配合使用,正如 Johannes Weiß 在评论中已经指出的那样,还有很多其他原因:

  • 集合具有非常丰富的 API 和种类繁多的实现,可以(在大多数情况下)轻松地相互换入和换出
  • 如果偶尔使用数组版本很有用,则可以将 Collection 轻松转换为数组
  • 许多集合的增长比数组的增长更优雅,这可能是性能问题
  • 集合非常适合泛型,而数组则相当糟糕
  • 正如 TofuBeer 指出的那样,数组协方差很奇怪,并且可以以任何对象都不会起作用的未执行方式起作用。集合以预期的方式处理协方差。
  • 需要根据任务手动调整数组大小,如果数组未满,您需要自己跟踪。如果需要调整数组的大小,您必须自己进行。

所有这些加在一起,我很少使用数组,只是更频繁地使用ArrayList。但是,我确实经常使用Lists(或者只是CollectionSet)。我最常使用数组的情况是,当存储的项目是原语时,将被插入、访问并用作原语。如果装箱和拆箱变得如此之快以至于它成为一个微不足道的考虑,我可能会重新考虑这个决定,但使用某些东西,以始终引用它的形式存储它更方便。 (即,'int' 而不是 'Integer'。)

【讨论】:

    【解决方案2】:

    这是过早未优化的情况:-)。你永远不应该做某事,因为你认为它会更好/更快/让你更快乐。

    ArrayList 有额外的开销,如果你不需要 ArrayList 的额外功能,那么使用 ArrayList 是一种浪费。

    对于您可以用 List 做的一些事情,还有 Arrays 类,这意味着 ArrayList 提供的功能比 Arrays 多,但事实并非如此。现在使用这些可能比使用 ArrayList 慢,但必须对其进行分析才能确定。

    你永远不应该尝试让某些东西变得更快,而不能确保它一开始就很慢......这意味着你应该继续使用 ArrayList 直到你发现它们是一个问题并减慢程序的速度。但是也应该涉及常识 - ArrayList 有开销,开销会很小但会累积。在分析器中不容易发现,因为这里有一点开销,那里有一点开销。所以常识会说,除非你需要 ArrayList 的特性,否则你不应该使用它,除非你想被千刀万剐(性能方面)。

    对于内部代码,如果您发现确实需要从数组更改为 ArrayList,在大多数情况下,机会是非常直接的([i] 变为 get(i),这将是 99% 的更改)。

    如果您使用的是 for-each 外观 (for(value : items) { }),那么也无需更改代码。

    另外,照你说的做:

    1) 访问速度相同,具体取决于您的环境。例如,Android VM 没有内联方法(据我所知,它只是一个直接的解释器),因此对其的访问会慢得多。 ArrayList 上的其他操作可能会导致速度变慢,这取决于您在做什么,而与 VM 无关(使用直接数组可能会更快,您必须再次分析或检查源以确定)。

    2) 包装器会增加正在使用的内存量。

    在分析某些内容之前,您不应该担心速度/内存,另一方面,除非您有充分的理由,否则您不应该选择较慢的选项。

    【讨论】:

    • 虽然我同意并 +1,但我认为您的回答过于冗长且过于宽泛。 :(只是说......
    • 是的,我在阅读其他帖子后回答了这个问题...所以试图一次解决很多问题:-)
    【解决方案3】:

    很多项目只是使用ArrayListHashMap 或其他任何方式来满足他们所有的收集需求。但是,让我对此提出一个警告。每当您创建类并在整个代码中使用它们时,如果可能,请参考它们实现的接口,而不是您用来实现它们的具体类。

    例如,而不是这样:

    ArrayList insuranceClaims = new ArrayList();
    

    这样做:

    List insuranceClaims = new ArrayList();
    

    甚至:

    Collection insuranceClaims = new ArrayList();
    

    如果您的其余代码仅通过它实现的接口(@98​​7654326@ 或Collection)知道它,那么如果您发现需要不同的实现,那么将其换成另一个实现会变得容易得多。就在一个月前,我看到了这种情况,当时我需要将常规的HashMap 换成一个实现,该实现将按照我在迭代所有这些时放入它们的顺序将这些项目返回给我。幸运的是,Jakarta Commons Collections 中提供了这样的东西,我只是将 A 替换为 B,只更改了一行代码,因为两者都实现了 Map。

    【讨论】:

      【解决方案4】:

      性能不应该是您最关心的问题。

      尽可能使用List接口,根据实际需求选择具体实现(ArrayList用于随机访问,LinkedList用于结构修改,...)。

      您应该关注性能。

      使用数组、System.arraycopyjava.util.Arrays 和其他低级别的东西来挤出最后一滴性能。

      【讨论】:

        【解决方案5】:

        不要总是盲目地使用不适合工作的东西。总是从使用 Lists 开始,选择 ArrayList 作为你的实现。这是一种更面向对象的方法。如果您不知道您特别需要一个数组,您会发现从长远来看,不将自己绑定到 List 的特定实现会更好。先让它工作,然后再优化。

        【讨论】:

        • 最后一部分,先让它工作,然后再优化,同样适用于数组......
        • 当然,将其应用于所有编程、算法以及存储。在大多数情况下,这种方法效果很好。
        猜你喜欢
        • 1970-01-01
        • 2011-12-26
        • 2012-02-14
        • 2023-02-09
        • 2013-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-07
        相关资源
        最近更新 更多