数组的好处

30

在我看来,与数组相比,列表的优点非常明显:

  • 泛型提供更精确的类型:List<Integer>, List<? extends Number>, List<? super Integer>
  • List接口具有许多有用的方法:addAllremove等。而对于数组来说,除了get/set之外的所有标准操作都必须通过传递到静态方法中以过程方式执行。
  • 集合提供不同的实现,如ArrayListLinkedList,不可修改和同步列表,可以在公共List接口下隐藏。
  • 面向对象的长度控制。

缺点方面,我只能提到缺乏语法糖和运行时类型检查。同时,支持两种结构需要频繁使用asListtoArray方法,这使代码不太易读。因此,我很想知道是否存在使用数组时我没有注意到的重要优点。

9个回答

21

数组在处理时间和内存占用方面更加高效。特别是当你在操作原始类型,比如 int 或者 long 时,这一点尤为明显,因为 List 需要将所有元素包装成对象(比如 Integer 或者 Long)。虽然 Java 5 引入了自动装箱的功能,减少了包装和拆包所需的代码量,但是它并没有解决性能问题,因为仍会创建包装对象。

然而,大多数应用程序可能并不会有与这些问题相关的性能瓶颈,所以在大多数情况下,List 和其他集合类应该可以胜任。在这些情况下,编程的便利性超过了内存或 CPU 使用增加的影响,List 是正确的选择。


4
数组更高效,但通常并不需要使用它们。在我编写的所有商业应用程序中,从未出现过可以通过将集合替换为数组来解决的性能问题。只有在数字计算工具中,我见过需要避免包装原始类型,因此需要坚持使用数组的情况。 - Christian Semrau
@Christian:同意,在大多数情况下,集合都能胜任。 - markusk
1
Java 泛型的一个重大缺陷是对原始类型的不支持。至少一些第三方库实现了针对原始类型的集合类,这可以消除装箱和拆箱的开销。 - josefx
许多系统中,数组也直接在硬件上实现。 - spongebob

12
如果您的列表不经常更改,使用List会为您永远不会使用的对象增加大量额外的负担。当您尝试运行需要进行优化的东西时,这是有帮助的。然而,这种额外的负担也使事情比仅有数组时更慢。但是,除非您知道需要数组所提供的收益,否则应该只使用Lists。

1
我猜这可以总结为以下几点:当你需要操作一组明显相关的对象时,请使用集合,否则请使用数组。 - Esko

5

速度。集合比简单的数组稍慢:内部大多数仍使用数组,但在其周围有额外的代码层。当然,除非您需要额外的性能,否则应该仍然使用集合。

另一个小优势是,使用数组可能更容易调用可变参数方法。不过,这绝不应该是选择一个而不是另一个的主要原因。


5

这里没有提到的一件事是:数组可以有n维,而列表只限于一维。您可以使用列表的列表,但语法(List<List<...>>)比[][]更冗长。


4
如果我忽略了使用数组的任何重要优点,请告诉我?
使用数组可以实现常数时间访问任何元素,且常数非常小。安全地访问数组元素只需要几条指令:几个加载、比较和分支。分支通常成功率接近100%,所以现代硬件在预测方面做得非常好。

我想更好地理解这里提到的指示,您能建议一个资源让我查看吗?谢谢。 - arin

2

除了其他回答,数组还有一个微妙的属性可以被认为是优于列表的优势。这可以通过以下代码来说明:

//This works
Integer[] ints = new Integer[4];
Number[] nums = ints;
//This doesn't work
List<Integer> intList = new ArrayList<Integer>();
List<Number> numList = intList; //Does not compile
List<Number> numList2 = (List<Number>)intList; //Does not compile either

虽然子类的数组是超类的数组,但子类的列表不是超类的列表(这样做是有好处的——如果允许的话,泛型会存在类型安全漏洞)。


1
实际上,数组存在类型安全漏洞,因为这是允许的,但漏洞只能在运行时而不是编译时被检测到。我可以按照您的代码进行nums[0] = new Double(8.9),并且它会编译通过。只有在运行时才会抛出异常。 - Daniel Martin
你不能在子类数组中存储任何超类的实例。因此,根据Liskov替换原则,子类数组不是超类数组。但是,正如@Daniel所指出的那样,这只能在运行时检测到。 - Christian Semrau
当然。通用代码中的限制旨在防止意外的ClassCastException,而数组则无法防止ArrayStoreException。但从编译器的角度来看,Integer[]是Number[],并且假定程序员知道实际的数组类型,因此允许强制转换。 - Eyal Schneider
啊,但没关系,因为之后的 numList.add(new Double(8.9)) 无法编译。 - Daniel Martin
@Daniel:请看上面的评论。它可以编译,但在运行时会抛出ArrayStoreException异常。数组施加较少的编译时限制(因此表现出的“好处”),但代价是失去了运行时类型安全性。 - Eyal Schneider
显示剩余2条评论

2

我想理论上讲,数组应该具有更好的性能,因为通用集合有额外的抽象层。但就业务应用而言,我认为使用通用集合比使用数组更有价值。


1

在以下情况下,数组更好:

  • 您知道您将使用数组中的固定元素数量
  • 您不需要更改数组的大小

数组比任何集合都要快:


类似于数组的集合:

  • ArrayList - 快速读取和添加到List的末尾。内部使用数组。如果必须增加List的大小,则速度较慢。
  • LinkedList - 快速添加到List的两侧。快速动态增加/减少大小。不使用内部数组。

结论:

我建议根据您的场景使用适当的集合。不要为了使用Array []而苦苦挣扎,因为Collections包提供了非常舒适的API,如add()addAll()等。


参考: 您可以在此处找到更详细的比较 -> "数组 vs ArrayList vs LinkedList vs..."


0

这真的取决于情况。数组非常快,但它们是固定大小的,如果需要处理的数据量非常大,则可能不适合。另一方面,集合的性能有不同的程度,具体取决于特定的子类。例如,ArrayList主要只是一个数组的包装器,因此应具有类似的迭代速度和内存需求。对于我自己,我通常在任何可能的地方使用Iterable<T>接口,因为这样可以给我的代码最大的灵活性,允许它处理内存中驻留的数组以及从文件或通过自定义可迭代/迭代器类获取的数据列表。当实际实例化我传入的Iterable对象时,这取决于具体情况;如果我知道大小并且一次可以放入内存中,那么我只需使用数组,而如果可能会增长,则我将使用ArrayList,如果需要快速插入两端,则我将使用LinkedList。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接