为什么Enumeration转换为ArrayList而不是java.utils中的List?
java.utils
包中的Collections.list()方法返回一个ArrayList<T>
而不是List<T>
是否有很好的理由?
显然ArrayList
是一个List
,但我的印象是,返回接口types而不是实现types通常是好的做法。
免责声明:我不是JDK作者。
我同意将自己的代码写入接口是正确的,但是如果要将可变集合返回给第三方,那么让第三方知道他们回来的是什么样的List
是很重要的。
对于各种操作, LinkedList
和ArrayList
是非常不同的,性能明智的。 例如,删除ArrayList
的第一个元素是O(n)
,但删除LinkedList
的第一个元素是O(1)
。
通过完全指定返回types,JDK作者正在以明确的代码传递额外的信息 ,说明他们回馈给您的对象types,因此您可以编写代码以正确使用此方法。 如果你真的需要一个LinkedList
,你知道你必须在这里指定一个。
最后,通过实现编写接口的主要原因是如果你认为实现将会改变。 JDK作者可能认为他们永远不会改变这种方法; 永远不会返回LinkedList
或Collections.UnmodifiableList
。 但是,在大多数情况下,您可能仍然会这样做:
List<T> list = Collections.list(enumeration);
当返回List
,您将会将程序推广到一个界面 ,这是一个非常好的做法。 但是,这种方法有其局限性。 例如,不能使用为ArrayList
定义的一些方法,而不在List
接口中存在 – 请参阅此答案以获取详细信息。
我在Java™教程中引用了APIdevise :
…可以返回任何 实现或扩展其中一个集合接口的types的对象。 这可以是扩展或实现这些接口之一的接口或专用types之一。
..从某种意义上说,返回值应该具有与input参数相反的行为: 最好是返回最具体的适用集合接口,而不是最一般的 。 例如,如果您确定始终会返回一个
SortedMap
,则应该将有关的方法指定为SortedMap
的返回types,而不是Map
。SortedMap
实例比普通的Map
实例更耗时,而且function更强大。 考虑到你的模块已经投入了时间来构buildSortedMap
,让用户访问其增加的function是很有意义的。 此外,用户将能够将返回的对象传递给需要SortedMap
方法以及接受任何Map
。
由于ArrayList
本质上是一个数组,当我需要一个“集合数组”时,它们是我的第一select。 所以,如果我想将枚举转换为列表,我的select将是一个数组列表。
在任何其他情况下,编写以下内容仍然有效:
List<T> list = Collections.list(e);
返回新创build的可变对象的“独占所有权”的函数通常应该是最具体的实用types; 那些返回不可变对象,特别是如果它们可能被共享,应该经常返回较不特定的types。
区分的原因是,在前一种情况下,对象总是能够产生指定types的新对象,并且由于接收者将拥有该对象,并且不知道接收者可能希望执行什么动作,所以在那里对于返回对象的代码通常不会知道是否有其他接口实现可以满足接收者的需求。
在后一种情况下,对象是不可变的这一事实意味着该函数可能能够确定一个替代types,它可以完成一个更复杂types可以做的所有事情,只要给出它的确切内容即可 。 例如, Immutable2dMatrix
接口可以由ImmutableArrayBacked2dMatrix
类和ImmutableDiagonal2dMatrix
类实现。 假定返回一个正方形Immutable2dMatrix
的函数可以决定返回一个ImmutableDiagonalMatrix
实例,如果主对angular线上的所有元素碰巧都是零,或者ImmutableArrayBackedMatrix
如果不是,则返回ImmutableDiagonalMatrix
实例。 前者types的存储空间要less很多,但是接收者不应该关心它们之间的差别。
返回Immutable2dMatrix
而不是具体的ImmutableArrayBackedMatrix
允许代码根据数组包含的内容来select返回types; 这也意味着如果应该返回数组的代码恰好持有Immutable2dMatrix
的适当实现,那么它可以简单地返回它,而不必构造一个新的实例。 当使用不可变对象时,这两个因素都可能是主要的“胜利”。
但是,在处理可变对象时,这两个因素都不起作用。 事实上,可变数组在生成主对angular线时可能没有任何元素,这并不意味着它永远不会有这样的元素。 因此,虽然ImmutableDiagonalMatrix
实际上是ImmutableDiagonalMatrix
的子types,但MutableDiagonalMatrix
不是MutableDiagonalMatrix
的子types,因为后者可以接受主对angular线上的存储,而前者不能。 此外,尽pipe不可变对象通常可以并且应该被共享,但是可变对象通常不能。 被要求使用特定内容初始化的新的可变集合的函数将需要创build一个新的集合,无论其后备存储是否匹配被请求的types。
在调用方法的时候有一个很小的开销,而不是直接在一个对象上。
这个开销通常不超过1或2个处理器指令。 如果JIT知道该方法是最终的,则调用方法的开销甚至更低。 这对大多数代码来说都是不可测的,但是对于java.utils中的低级别的方法,可能会在某些代码中使用。
正如在其他答案中已经指出的那样,返回对象的具体types(即使隐藏在接口后面)影响使用它的代码的性能。 这种性能变化可能非常大,致使调用软件无法工作。
很显然, java.utils
的作者无法知道所有调用Collections.list()的软件如何改变结果,如果改变了Collections.list()的植入方式,则无法重新testing这个软件。 因此,即使types系统允许,它们也不会改变Collections.list()的植入来返回不同types的List。
在编写你自己的软件时,你(希望)有自动化的testing,涵盖你所有的代码,并且很好地理解你的代码如何相互关联,包括知道性能是一个问题。 在软件devise不断变化的情况下,能够对方法进行更改,而无需更改调用者,这是非常有价值的。
所以这两套折衷是非常不同的。