(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

我知道,对于(1),List接口的实现可以交换。似乎(1)通常在应用程序中使用,无论是否需要(我自己总是使用它)。

我想知道是否有人使用(2)?

此外,情况实际上需要使用(1)而不是(2)的频率是多少(请给我一个例子)(即(2)是不够的..除了编码到接口和最佳实践等)。


当前回答

列表接口有几个不同的类——ArrayList和LinkedList。LinkedList用于创建索引集合,ArrayList用于创建排序列表。你可以在你的参数中使用它,但是你可以允许其他使用你的代码,库等的开发人员使用不同类型的列表,而不仅仅是你在这个方法中使用的

ArrayList<Object> myMethod (ArrayList<Object> input) {
   // body
}

你只能在ArrayList中使用它,而不是LinkedList,但是你可以允许在其他地方使用它的方法,这只是你的选择,所以使用接口可以允许它:

List<Object> myMethod (List<Object> input) {
   // body
}

在这个方法参数中,你可以使用任何你想使用的List类:

List<Object> list = new ArrayList<Object> ();

list.add ("string");

myMethod (list);

结论:

尽可能在任何地方使用接口,不要限制您或其他人使用他们想要使用的不同方法。

其他回答

我会说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的方式。

将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接口设计得很好,您应该使用它们。

例如,你可能认为LinkedList是应用程序的最佳选择,但后来又认为ArrayList可能是更好的选择。

Use:

List list = new ArrayList(100); // will be better also to set the initial capacity of a collection 

而不是:

ArrayList list = new ArrayList();

供参考:

(主要用于收集图)

当你写List时,你实际上告诉,你的对象只实现了List接口,但你没有指定你的对象属于什么类。

在编写ArrayList时,指定对象类是一个可调整大小的数组。

因此,第一个版本使您的代码将来更加灵活。

看看Java文档:

类ArrayList——List接口的可调整大小的数组实现。

接口列表-一个有序的集合(也称为序列)。该接口的用户可以精确控制每个元素在列表中的插入位置。

数组—容器对象,保存固定数量的单一类型的值。