(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
我理解,使用(1),可以交换List接口的实现。似乎(1)通常在应用程序中使用而不管需要(我自己总是使用它)。
我想知道是否有人使用(2)?
此外,这种情况实际上需要使用(1)over(2)(即,(2)不足以进行编码接口和最佳实践等)。
几乎总是第一个优先于第二个。第一个优点是List
的实现可以改变(例如LinkedList
),而不会影响代码的其余部分。这对ArrayList
来说将是一项艰巨的任务,不仅因为你需要将ArrayList
更改为LinkedList
,而且因为你可能已经使用了ArrayList
特定的方法。
你可以阅读有关qazxsw poi实施qazxsw poi的信息。你可以从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
上使用here。更多ArrayList
。我想知道是否有人使用(2)
有时是(很少读)。当我们需要的方法是LinkedList
的实现的一部分,但不是here接口的一部分。例如ArrayList
。
此外,多久(我可以得到一个例子)情况实际上需要使用(1)over(2)
几乎总是你更喜欢选项(1)。这是OOP中的经典设计模式,您总是尝试将代码从特定的实现和程序与接口分离。
List是一个接口。它没有方法。在List引用上调用方法时。它实际上在两种情况下都调用了ArrayList的方法。
并且将来你可以将List
更改为ensureCapacity
或其他实现List接口的类型。
有人再次问这个问题(重复),这让我对这个问题有了更深入的了解。
List obj = new ArrayList<>
如果我们使用字节码查看器(我使用List obj = new LinkList<>
),我们将看到以下列表(仅列表初始化和分配):
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");
}
对于alist:
http://asm.ow2.org/eclipse/index.html
不同之处在于列表最终调用INVOKEINTERFACE,而List调用INVOKEVIRTUAL。根据Bycode Outline插件参考,
invokeinterface用于调用Java接口中声明的方法
而invokevirtual
调用除接口方法(使用invokeinterface),静态方法(使用invokestatic)以及invokespecial处理的少数特殊情况之外的所有方法。
总之,invokevirtual在调用接口时将objectref从堆栈中弹出
解释器在操作数堆栈中弹出“n”项,其中“n”是取自字节码的8位无符号整数参数。这些项中的第一项是objectref,它是对正在调用其方法的对象的引用。
如果我理解正确,差异基本上是每种方式检索objectref。
我知道哪个(2)可以更好的唯一情况是使用GWT,因为它减少了应用程序占用空间(不是我的想法,但谷歌网络工具包团队这样说)。但是对于在JVM(1)内部运行的常规Java可能总是更好。
我会说1是首选,除非
我的猜测是,在99%的情况下,你可以使用List,这是首选。
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
,或 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
removeAll
界面有几个不同的类 - add(null)
和List
。 ArrayList
用于创建索引集合和LinkedList
- 创建排序列表。因此,您可以在参数中使用任何一个,但是您可以允许使用您的代码,库等的其他开发人员使用不同类型的列表,而不仅仅是您使用的列表,因此,在此方法中
LinkedList
你只能使用ArrayList
,而不是ArrayList<Object> myMethod (ArrayList<Object> input) {
// body
}
,但你可以允许在其使用方法的其他地方使用任何ArrayList
类,这只是你的选择,所以使用接口可以允许它:
LinkedList
在此方法参数中,您可以使用要使用的任何List
类:
List<Object> myMethod (List<Object> input) {
// body
}
结论:
尽可能在任何地方使用接口,不要限制您或其他人使用他们想要使用的不同方法。
我想知道是否有人使用(2)?
是。但很少有正当理由(IMO)。
人们因为使用here而使用ArrayList
而被烧毁:
ArrayList
或List
这样的实用方法不会返回Collections.singletonList(...)
。Arrays.asList(...)
API中的方法不保证返回相同类型的列表。例如,有人被烧毁,在ArrayList
,海报有“切片”的问题,因为List
没有返回https://stackoverflow.com/a/1481123/139985 ......他设计了他的代码使用ArrayList.sublist(...)
作为他所有列表变量的类型。他最终通过将子列表复制到新的ArrayList
来“解决”问题。
你需要知道ArrayList
表现如何的论点在很大程度上通过使用ArrayList
标记界面来解决。是的,它有点笨重,但替代方案更糟糕。
此外,情况多久经常需要使用(1)超过(2)(即,(2)不足以“对接口进行编码”和最佳实践等)
问题的“多久”部分是客观上无法回答的。
(我可以举一个例子)
有时,应用程序可能要求您使用List
API中不在RandomAccess
API中的方法。例如,ArrayList
,List
或ensureCapacity(int)
。 (最后一个只会在你创建一个ArrayList的子类型时才会出现,该子类型声明该方法为trimToSize()
。)
这是编写到类而不是接口IMO的唯一合理原因。
(从理论上讲,在某些情况下......在某些平台上......你的性能会略有改善......但除非你真的需要最后的0.05%,否则不值得这样做。这不是一个合理的理由,国际海事组织。)
如果您不知道随机访问是否有效,则无法编写有效的代码。
这是一个有效的观点。但是,Java提供了更好的方法来处理它;例如
removeRange(int, int)
如果使用未实现public
的列表调用它,则会出现编译错误。
您还可以动态测试...使用public <T extends List & RandomAccess> void test(T list) {
// do stuff
}
...如果静态类型太笨拙。您甚至可以编写代码以使用不同的算法(动态),具体取决于列表是否支持随机访问。
请注意,RandomAccess
不是唯一实现instanceof
的列表类。其他包括ArrayList
,RandomAccess
和CopyOnWriteList
。
我见过人们对Stack
提出同样的论点(因为Vector
没有实现它)......但上面的方法也解决了这个问题。 (在某种程度上它可以使用运行时类型解决。如果任何元素不可序列化,Serializable
将无法序列化。)
例如,您可能会认为List
是您的应用程序的最佳选择,但后来决定ArrayList
可能是出于性能原因的更好选择。
使用:
LinkedList
代替:
ArrayList
以供参考:
(主要用于收集图)
在类型为Set的变量中存储对List list = new ArrayList(100); // will be better also to set the initial capacity of a collection
或ArrayList list = new ArrayList();
的引用被认为是好的样式。
HashSet
这样,如果您决定使用TreeSet
,则必须只更改一行。
此外,对集合进行操作的方法应指定Set类型的参数:
Set<String> names = new HashSet<String>();
然后,该方法可用于所有集合实现。
理论上,我们应该对链表做出相同的建议,即在List类型的变量中保存LinkedList引用。但是,在Java库中,List接口对于TreeSet
和public static void print(Set<String> s)
类都是通用的。特别是,它具有获取和设置随机访问的方法,即使这些方法对链表非常低效。
如果您不知道随机访问是否有效,则无法编写有效的代码。
这显然是标准库中的一个严重的设计错误,因此我不建议使用List接口。
要查看错误是多么令人尴尬,请查看Collections类的ArrayList
方法的源代码。该方法采用List参数,但二进制搜索对链表没有意义。然后代码笨拙地试图发现列表是否是链表,然后切换到线性搜索!
LinkedList
界面和binarySearch
界面设计得很好,您应该使用它们。
如果代码是列表的“所有者”,我使用(2)。例如,对于仅本地变量,这是真的。没有理由使用抽象类型Set
而不是Map
。另一个证明所有权的例子:
List
当您编写ArrayList
时,您实际上告诉您,您的对象仅实现了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);
}
}
接口,但您没有指定您的对象所属的类。
编写List
时,指定对象类是可调整大小的数组。
因此,第一个版本将使您的代码在未来更加灵活。
看看Java文档:
List
- ArrayList
接口的可调整大小的数组实现。
Class ArrayList
- 有序集合(也称为序列)。该接口的用户可以精确控制列表中每个元素的插入位置。
List
- 容器对象,它包含固定数量的单一类型的值。
(3)Collection myCollection = new ArrayList();
我通常使用这个。只有我需要List方法,我才会使用List。与ArrayList相同。你总是可以切换到更“窄”的界面,但你不能切换到更“宽”。
我认为使用(2)的人不知道Interface List
或Array
。或者他们真的必须使用Liskov substitution principle。
实际上有些情况下(2)不仅是首选而是强制性的,我很惊讶,没有人在这里提到这一点。
序列化!
如果你有一个可序列化的类,并且你希望它包含一个列表,那么你必须声明该字段是一个具体和可序列化的类型,如Dependency inversion principle,因为ArrayList
接口不扩展ArrayList
显然大多数人不需要序列化而忘记了这一点。
一个例子:
List