在Java中input列表与typesArrayList

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

我明白,在(1)中, List接口的实现可以交换。 看来,(1)通常在应用程序中使用而不pipe需要(我自己总是使用它)。

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

另外,这种情况实际上需要多久使用(1)over(2)(例如where(2)不足),除了接口最佳实践的 编码外,

几乎总是第一个比第二个更受欢迎。 第一个优点是List的实现可以改变(例如LinkedList ),而不影响其余的代码。 这对于一个ArrayList是一件困难的事情,不仅因为你需要将ArrayList改变为LinkedList ,而且还因为你可能已经使用了ArrayList特定方法。

你可以在这里阅读关于List实现。 你可以从一个ArrayList开始,但不久之后发现另一个实现更合适。

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

是。 但很less有一个很好的理由。

有时候人们会因为使用ArrayList而被烧毁,因为他们应该使用List

  • Collections.singletonList(...)Arrays.asList(...)这样的工具方法不会返回一个ArrayList

  • List API中的方法不保证返回相同types的列表。

例如,在https://stackoverflow.com/a/1481123/139985海报有“切片”的问题,因为;ArrayList.sublist(...)不返回一个ArrayList …他devise了他的代码使用ArrayList作为其所有列表variables的types。 他最终通过将子列表复制到一个新的ArrayList来“解决”这个问题。

你需要知道List行为的论点主要是通过使用RandomAccess标记接口来解决的。 是的,这有点笨重,但另一种更糟。

此外,情况实际上需要多长时间使用(1)over(2)(即,where(2)不足以……“编码接口”和最佳实践等)

问题的“多频繁”部分客观上是无法回答的。

(我能举个例子)

偶尔,应用程序可能会要求您使用ArrayList API中不在 List API中的方法。 例如, ensureCapacity(int)trimToSize()removeRange(int, int) 。 (最后一个只会在你创build了一个声明该方法public的ArrayList的子types时才会出现。

这是编写到类,而不是接口,IMO的唯一合理的原因。

(从理论上讲,在某些情况下,在某些平台上,性能可能会略有提高…但是,除非您真的需要最后的0.05%,否则不值得这样做。合理的理由,IMO。)


如果不知道随机访问是否有效,则无法编写高效的代码。

这是一个有效的观点。 但是,Java提供了更好的方法来处理这个问题。 例如

 public <T extends List & RandomAccess> void test(T list) { // do stuff } 

如果你用一个没有实现RandomAccess的列表来调用它,你会得到一个编译错误。

你也可以dynamic地testing…使用instanceof …如果静态types太尴尬。 甚至可以编写代码来使用不同的algorithm(dynamic),具体取决于列表是否支持随机访问。

请注意, ArrayList不是唯一实现RandomAccess列表类。 其他的还有CopyOnWriteListStackVector

例如,您可能会决定LinkedList是您的应用程序的最佳select,但稍后决定ArrayList可能是一个更好的select性能的原因。

使用:

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

代替:

 ArrayList list = new ArrayList(); 

以供参考:

在这里输入图像描述

(主要张贴收集图)

HashSetTreeSet的引用存储在Settypes的variables中被认为是很好的风格

Set<String> names = new HashSet<String>();

这样,如果决定使用TreeSet ,则必须只更改一行。

另外,对集合进行操作的方法应该指定typesSet的参数:

public static void print(Set<String> s)

然后该方法可以用于所有设置的实现

理论上,我们应该对链接列表做出相同的build议,即将LinkedList引用保存到Listtypes的variables中。 但是,在Java库中,List接口对于ArrayListLinkedList类都是通用的。 特别是,它已经获得并设置了随机访问的方法,即使这些方法对于链接列表来说效率非常低。

如果不知道随机访问是否有效, 则无法编写高效的代码

这是标准库中的一个严重的devise错误,我不能推荐使用List接口。

要查看错误是多么令人尴尬,请查看Collections类的binarySearch方法的源代码。 该方法采用List参数,但二进制search对链接列表没有意义。 然后代码笨拙地试图发现列表是否是一个链表,然后切换到线性search!

Set界面和Map界面,devise得很好,你应该使用它们。

我使用(2)如果代码是列表的“所有者”。 例如,对于仅限地方的variables,这是正确的。 没有理由使用抽象typesList而不是ArrayList 。 certificate所有权的另一个例子:

 public class Test { // This object is the owner of strings, so use the concrete type. private final ArrayList<String> strings = new ArrayList<>(); // This object uses the argument but doesn't own it, so use abstract type. public void addStrings(List<String> add) { strings.addAll(add); } // Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list. public List<String> getStrings() { return Collections.unmodifiableList(strings); } // Here we create a new list and give ownership to the caller. Use concrete type. public ArrayList<String> getStringsCopy() { return new ArrayList<>(strings); } } 

我认为使用(2)的人不知道Liskovreplace原理或依赖倒置原理 。 或者他们真的必须使用ArrayList

(3)Collection myCollection = new ArrayList();

我通常使用这个。 只有当我需要List方法时,我才会使用List。 和ArrayList一样。 你总是可以切换到更“窄”的界面,但是你不能切换到更“宽”。

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

当你写ArrayList ,你指定你的对象类是一个可resize的数组。

所以,第一个版本将来会使你的代码更加灵活。

看看Java文档:

ArrayListList接口的可resize的实现。

接口List – 一个有序的集合(也称为序列)。 这个接口的用户可以精确地控制每个元素插入到列表中的哪个位置。

Array – 容器对象,其中包含固定数量的单个types的值。

其实有些情况下,(2)不仅是首选,而且是强制性的,我感到非常惊讶,没有人在这里提到这一点。

序列化!

如果您有一个可序列化的类,并且您希望它包含一个列表,那么您必须将该字段声明为像ArrayList这样的具体和可序列化的types,因为List接口不会扩展java.io.Serializable

显然大多数人不需要序列化,忘记这一点。

一个例子:

 public class ExampleData implements java.io.Serializable { // The following also guarantees that strings is always an ArrayList. private final ArrayList<String> strings = new ArrayList<>(); 

出于以下两点:

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

首先是一般的首选。 由于您将仅使用来自List接口的方法,因此您可以自由地使用List其他实现,例如LinkedList 。 所以它把你从具体的实现中解耦出来。 现在有两点值得一提:

  1. 我们应该总是编程接口。 更多在这里 。
  2. 你几乎总是通过LinkedList来使用ArrayList 。 更多在这里 。

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

是的,有时(很less读)。 当我们需要的方法是ArrayList实现的一部分,但不是接口List一部分。 例如ensureCapacity

另外,这种情况实际上需要多久使用一次(我可以请一个例子):(1)超过(2)

几乎总是你喜欢选项(1)。 这是OOP中的一个经典devise模式,您总是试图将您的代码从特定实现和程序解耦到接口。

我知道在哪里可以更好地使用GWT,因为它减less了应用程序的占用(不是我的想法,但是google web toolkit团队这样说)。 但是对于在JVM(1)内部运行的普通Java,可能总是更好。

List是一个接口,它没有方法。 当您调用List引用的方法时。 它实际上在两种情况下调用ArrayList的方法。

未来,您可以将List obj = new ArrayList<>更改为List obj = new LinkList<>或实现List接口的其他types

我会说,1是首选,除非

  • 你是依赖于可选行为的实现*在ArrayList中,那么明确使用ArrayList的情况就更加清楚了
  • 您将在需要ArrayList的方法调用中使用ArrayList,可能用于可选行为或性能特征

我的猜测是,在99%的情况下,你可以通过列表,这是首选。

  • 例如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 ),我们可以看到我们列表片段的以下内容(仅列表初始化和赋值):

  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 

不同的是列表最终调用INVOKEINTERFACEaList调用INVOKEVIRTUAL 。 根据Bycode Outline Plugin参考,

invokeinterface用于调用在Java接口中声明的方法

而invokevirtual

调用除接口方法(使用invokeinterface),静态方法(使用invokestatic)和invokespecial处理的less数特殊情况以外的所有方法。

总之,invokevirtual将objectref从堆栈中popup,同时用于invokeinterface

解释器从操作数堆栈中popup“n”个项目,其中“n”是从字节码取得的8位无符号整数参数。 这些项目中的第一个是objectref,是对其方法被调用的对象的引用。

如果我正确地理解了这一点,那么区别在于每个方法如何检索objectref