为什么Enumeration转换为ArrayList而不是java.utils中的List?

时间:2015-09-02 15:05:49

标签: java arraylist collections return-type

java.utils包中的Collections.list()方法是否有理由返回ArrayList<T>而不是List<T>

显然,ArrayListList,但我认为返回接口类型而不是实现类型通常是一种好习惯。

4 个答案:

答案 0 :(得分:54)

免责声明:我不是JDK作者。

我同意将自己的代码写入接口是正确的,但如果您要将可变集合返回给第三方,那么让我们知道第三方知道他们回来了什么List

对于各种操作,

LinkedListArrayList在性能方面非常不同。例如,删除ArrayList的第一个元素是O(n),但删除LinkedList的第一个元素是O(1)

通过完全指定返回类型,JDK作者在明确的代码中传达额外的信息,关于他们给你的对象是什么类型,所以你可以编写代码来使用这个方法得当。如果你真的需要一个LinkedList,你知道你必须在这里指定一个。

最后,在实现上编写接口代码的主要原因是您认为实现会发生变化。 JDK的作者可能认为他们永远不会改变这种方法;它永远不会返回LinkedListCollections.UnmodifiableList。但是,在大多数情况下,您可能仍会这样做:

List<T> list = Collections.list(enumeration);

答案 1 :(得分:31)

返回List时,您将程序推广到界面,这是一种非常好的做法。但是,这种方法有其局限性。例如,您无法使用为ArrayList定义但在List界面中不存在的某些方法 - 有关详细信息,请参阅this answer

我引用了The Java™Tutorials中的API Design

  

...可以返回任何类型的对象实现扩展其中一个集合接口。这可以是接口之一,也可以是扩展或实现其中一个接口的特殊用途类型。

     

..从某种意义上说,返回值应该具有与输入参数相反的行为: best 返回最具体的适用集合接口,而不是最常用的。例如,如果您确定始终返回SortedMap,则应该为相关方法提供SortedMap而非Map的返回类型。 SortedMap实例构建比普通Map实例更耗时,并且更强大。鉴于您的模块已经投入时间来构建SortedMap,因此让用户访问其增强的功能是很有意义的。此外,用户将能够将返回的对象传递给需要SortedMap的方法,以及接受任何Map的方法。

由于ArrayList本质上是一个数组,所以当我需要一个“集合数组”时,它们是我的第一选择。因此,如果我想将枚举转换为列表,我的选择将是数组列表。

在任何其他情况下,写作仍然有效:

List<T> list = Collections.list(e);

答案 2 :(得分:6)

返回新创建的可变对象的“独占所有权”的函数通常应该是最具体的类型;返回不可变对象的那些,特别是如果它们可能被共享,通常应返回不太具体的类型。

区别的原因在于,在前一种情况下,对象始终能够生成所指示类型的新对象,并且由于收件人将拥有该对象,并且无法告知收件人可能希望采取的操作执行时,返回对象的代码通常无法知道任何替代接口实现是否能满足接收​​者的需求。

在后一种情况下,对象是不可变的这一事实意味着该函数可能能够识别一种替代类型,该类型可以执行更复杂类型可以执行的所有内容给定其确切内容。例如,Immutable2dMatrix类可以实现ImmutableArrayBacked2dMatrix接口和ImmutableDiagonal2dMatrix类。如果主对角线以外的所有元素都为零,则假定返回方Immutable2dMatrix的函数可以决定返回ImmutableDiagonalMatrix实例,如果不是,则返回ImmutableArrayBackedMatrix。前一种类型会占用更少的存储空间,但收件人不应该关心它们之间的区别。

返回Immutable2dMatrix而不是具体的ImmutableArrayBackedMatrix允许代码根据数组包含的内容选择返回类型;它还意味着如果应该返回数组的代码恰好持有Immutable2dMatrix的合适实现,它可以简单地返回它,而不是必须构造一个新实例。在处理不可变对象时,这两个因素都可能是主要的“胜利”。

然而,当使用可变对象时,两个因素都不起作用。可变数组在生成时可能没有任何元素离开主对角线并不意味着它永远不会有任何这样的元素。因此,虽然ImmutableDiagonalMatrix实际上是Immutable2dMatrix的子类型,但MutableDiagonalMatrix不是Mutable2dMatrix的子类型,因为后者可以接受主对角线上的商店前者不能。此外,虽然不可变对象通常可以并且应该被共享,但是可变对象通常不能。要求使用特定内容初始化的新可变集合的函数将需要创建新集合,无论其后备存储是否与所请求的类型匹配。

答案 3 :(得分:1)

调用方法var接口而不是直接在对象上有一个开销。

此开销通常不超过1或2个处理器指令。如果JIT知道方法是最终的,那么调用方法的开销会更低。对于你和我的大多数代码来说,这是不可测量的,但是对于java.utils中的低级方法,可能会在一些问题中使用它。

同样在其他答案中已经指出,返回的对象的具体类型(即使隐藏在接口后面)也会影响使用它的代码的性能。这种性能变化可能非常大,因此调用软件无法正常工作。

显然java.utils的作者无法知道调用Collections.list()的所有软件对结果的影响,如果他们更改了Collections.list的植入,则无法重新测试该软件。 ()。 因此,即使类型系统允许,它们也不会更改Collections.list()的植入以返回不同类型的List!

在编写自己的软件时,您(希望)拥有涵盖所有代码的自动化测试,并且很好地理解代码如何相互关联,包括知道性能问题。能够在不改变调用者的情况下对方法进行更改是非常有价值的,而软件的设计正在发生变化。

因此,两组权衡取舍非常不同。