(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
我知道,对于(1),List接口的实现可以交换。似乎(1)通常在应用程序中使用,无论是否需要(我自己总是使用它)。
我想知道是否有人使用(2)?
此外,情况实际上需要使用(1)而不是(2)的频率是多少(请给我一个例子)(即(2)是不够的..除了编码到接口和最佳实践等)。
(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
我知道,对于(1),List接口的实现可以交换。似乎(1)通常在应用程序中使用,无论是否需要(我自己总是使用它)。
我想知道是否有人使用(2)?
此外,情况实际上需要使用(1)而不是(2)的频率是多少(请给我一个例子)(即(2)是不够的..除了编码到接口和最佳实践等)。
当前回答
将HashSet或TreeSet的引用存储在Set类型的变量中被认为是很好的风格。
Set<String> names = new HashSet<String>();
这样,如果您决定使用TreeSet,则只需更改一行。
同样,操作Set的方法应该指定Set类型的参数:
(Set<String> s)
然后,该方法可用于所有set实现。
理论上,我们应该对链表提出同样的建议,即保存 LinkedList在List类型变量中的引用。然而,在Java库中,List接口对于ArrayList和LinkedList类都是通用的。特别是,它有用于随机访问的get和set方法,尽管这些方法对于链表来说效率非常低。
如果你不知道随机访问是否有效,你就不能写出有效的代码。
这显然是标准库中的一个严重设计错误,我不建议使用 List接口就是因为这个原因。
看看这个错误有多尴尬吧 Collections类的binarySearch方法的源代码。这个方法需要 列表参数,但二分搜索对链表没有意义。然后代码就笨拙了 尝试发现列表是否是链表,然后切换到线性搜索!
Set接口和Map接口设计得很好,您应该使用它们。
其他回答
出以下两种:
(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
第一种通常是首选。因为你将只使用List接口的方法,它为你提供了使用其他List实现的自由,例如未来的LinkedList。所以它将你与特定的实现分离。现在有两点值得一提:
我们应该始终按照接口编程。更多的在这里。 你几乎总是会使用数组列表而不是LinkedList。更多的在这里。
我想知道是否有人使用(2)
有时会(很少阅读)。当我们需要的方法是ArrayList实现的一部分而不是接口List的一部分时。例如ensureCapacity。
还有,这种情况发生的频率(以及我能得到一个例子吗) 实际上需要使用(1)而不是(2)
这是OOP中的一个经典设计模式,你总是试图将你的代码从特定的实现和程序解耦到接口。
我会说1是首选,除非
你依赖于ArrayList中可选行为*的实现,在这种情况下显式使用ArrayList更清楚 你将在一个需要ArrayList的方法调用中使用ArrayList,可能用于可选的行为或性能特征
我的猜测是,在99%的情况下,您可以使用List,这是首选。
为实例removeAll,或add(null)
有人又问了这个问题(重复),这让我在这个问题上更深入了一些。
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
ArrayList<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("b");
}
如果我们使用字节码查看器(我使用http://asm.ow2.org/eclipse/index.html) weĺl看到以下(仅列表初始化和赋值)为我们的列表片段:
L0
LINENUMBER 9 L0
NEW ArrayList
DUP
INVOKESPECIAL ArrayList.<init> () : void
ASTORE 1
L1
LINENUMBER 10 L1
ALOAD 1: list
LDC "a"
INVOKEINTERFACE List.add (Object) : boolean
POP
L2
LINENUMBER 11 L2
ALOAD 1: list
LDC "b"
INVOKEINTERFACE List.add (Object) : boolean
POP
对于alist来说:
L3
LINENUMBER 13 L3
NEW java/util/ArrayList
DUP
INVOKESPECIAL java/util/ArrayList.<init> ()V
ASTORE 2
L4
LINENUMBER 14 L4
ALOAD 2
LDC "a"
INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
POP
L5
LINENUMBER 15 L5
ALOAD 2
LDC "b"
INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
POP
区别在于list调用INVOKEINTERFACE,而aList调用invokvirtual。根据Bycode大纲插件参考,
invokeinterface用于调用Java中声明的方法 接口
虽然invokevirtual
调用除接口方法(使用 Invokeinterface)、静态方法(使用invokestatic)等等 特殊情况由invokspecial处理。
总之,invokvirtual从栈中弹出objectref,而对于invokeinterface
解释器从操作数堆栈中取出'n'项,其中'n'是8位无符号 从字节码获取的整数参数。第一项是 Objectref,对正在调用其方法的对象的引用。
如果我理解正确的话,区别基本上是每种方法检索objectref的方式。
List几乎总是优先于ArrayList,因为,例如,List可以被转换为LinkedList而不影响其余的代码库。
如果使用ArrayList而不是List,就很难将ArrayList实现更改为LinkedList,因为代码库中已经使用了特定于ArrayList的方法,这也需要重构。
你可以在这里阅读关于List实现的信息。
您可以从ArrayList开始,但很快就会发现另一种实现是更合适的选择。
我认为使用(2)的人不知道利斯科夫替换原则或依赖反转原则。或者必须使用数组列表。