在Java中键入List vs type ArrayList

kji*_*kji 537 java interface list decoupling

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

(2) ArrayList<?> myList = new ArrayList<?>();
Run Code Online (Sandbox Code Playgroud)

我理解,使用(1),可以交换List接口的实现.似乎(1)通常在应用程序中使用而不管需要(我自己总是使用它).

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

此外,经常(并且我可以得到一个例子)情况实际上需要使用(1)over(2)(即where(2)不足以对接口最佳实践等进行编码)

kgi*_*kis 426

几乎总是第一个优先于第二个.第一个优点是List可以改变(ArrayList例如)的实现,而不会影响代码的其余部分.这将是一项艰巨的任务List,不仅因为你需要改变LinkedListArrayList任何地方,还因为你可能已经使用了List特定的方法.

你可以阅读有关ArrayList实现此处.您可以从一开始LinkedList,但不久之后发现另一个实现更合适.

  • “而且还因为您可能已使用ArrayList特定方法”-这不是使用ArrayList的参数吗?如果使用List,则不能使用ArrayList特定的方法。 (3认同)
  • 你能详细说明改变清单的实施吗?使用我的代码作为示例,将myList更改为LinkedList,是否仍然需要在myList上调用new LinkedList()? (2认同)
  • 是的,这将是您需要的唯一代码更改.与在每种方法中将ArrayList更改为LinkedList相比较.更不用说必须替换一个ArrayList only方法. (2认同)
  • @Blrp:您确实需要使用特定于ArrayList的方法的情况是有限的。在这种情况下,您可以非常轻松地将List轻松转换为ArrayList。最常见的是无意中使用了ArrayList方法,而不必这样做。很难将ArrayList更改为List或其他List实现。 (2认同)
  • 需要注意的是“特定于ArrayList的方法”不仅限于ArrayList中使用的方法,而且它们的返回类型也被更改(如果内部返回类型发生更改,则会在Java版本之间引起NoSuchMethodException)。一个例子是Java 7和8之间在ConcurrentHashMap中的keySet返回类型的更改。 (2认同)

Ste*_*n C 110

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

是.但很少有正当理由(IMO).

人们会因为ArrayList他们应该使用的List时间而被烧毁:

  • 实用方法喜欢Collections.singletonList(...)Arrays.asList(...)不返回ArrayList.

  • ListAPI中的方法不保证返回相同类型的列表.

例如,某人被烧毁,在/sf/answers/103678641/中,海报遇到"切片"问题,因为ArrayList.sublist(...)没有返回ArrayList...并且他设计了他的代码ArrayList用作类型他所有的列表变量.他最终通过将子列表复制到一个新的"解决"问题ArrayList.

您需要知道如何List通过使用RandomAccess标记接口来解决行为的问题.是的,它有点笨重,但替代方案更糟糕.

此外,情况多久经常需要使用(1)over(2)(即where(2)不足以"对接口进行编码"和最佳实践等)

问题的"多久"部分是客观上无法回答的.

(我可以举一个例子)

有时,应用程序可能需要您使用的方法ArrayListAPI是不是该在ListAPI.例如ensureCapacity(int),trimToSize()removeRange(int, int).(最后一个只有在你创建了一个声明方法的ArrayList子类型时才会出现public.)

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

(从理论上讲,在某些情况下......在某些平台上......你的性能会略有改善......但除非你真的需要最后的0.05%,否则不值得这样做.这不是一个合理的理由,国际海事组织.)


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

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

public <T extends List & RandomAccess> void test(T list) {
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

如果使用未实现的列表调用它,RandomAccess则会出现编译错误.

instanceof如果静态类型太笨拙,你也可以动态测试......使用... 您甚至可以编写代码以使用不同的算法(动态),具体取决于列表是否支持随机访问.

请注意,这ArrayList不是唯一实现的列表类RandomAccess.其他包括CopyOnWriteList,StackVector.

我已经看到人们提出相同的论点Serializable(因为List没有实现它)......但上面的方法也解决了这个问题.(在某种程度上,它可以使用运行时类型完全解决.ArrayList如果任何元素不可序列化,则会导致序列化失败.)


Max*_*tin 39

例如,您可能认为a LinkedList是您的应用程序的最佳选择,但后来决定ArrayList可能是性能原因的更好选择.

使用:

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

代替:

ArrayList list = new ArrayList();
Run Code Online (Sandbox Code Playgroud)

以供参考:

在此输入图像描述

(主要用于收集图)


naz*_*art 24

将引用存储在一个或类型为Set的变量中被认为是好的样式.HashSetTreeSet

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

这样,如果您决定使用TreeSet替换,则只需更改一行.

此外,对集合进行操作的方法应指定Set类型的参数:

public static void print(Set<String> s)

然后,该方法可用于所有集合实现.

理论上,我们应该对链表做出相同的建议,即在List类型的变量中保存LinkedList引用.但是,在Java库中,List接口ArrayList对于LinkedList类和类都是通用的.特别是,它具有获取和设置随机访问的方法,即使这些方法对于链表非常低效.

可以不写高效的代码,如果你不知道随机访问是否是有效与否.

这显然是标准库中的一个严重的设计错误,因此我不建议使用List接口.

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

Set接口和Map接口,都是精心设计的,你应该使用它们.

  • 赞成*具体说明隐藏ArrayList与LinkedList后面的问题是什么问题. (5认同)
  • 但是请注意,有一个名为“ RandomAccess”的标记器接口,该接口适用于所有具有有效随机访问权限的标准“ List”子类。例如,“ ArrayList”实现了这一点,而“ LinkedList”则没有实现。您可以使用它来选择是否需要随机访问的算法。 (2认同)

maz*_*ork 13

如果代码是列表的"所有者",我使用(2).例如,对于仅本地变量,这是真的.没有理由使用抽象类型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);
    }
}
Run Code Online (Sandbox Code Playgroud)


wzb*_*zon 11

List实际上,当您编写时,您的对象List仅实现了接口,但您没有指定对象所属的类.

编写时ArrayList,指定对象类是可调整大小的数组.

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

看看Java文档:

ArrayList -List接口的可调整大小的数组实现.

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

Array - 容器对象,其包含固定数量的单个类型的值.


Pav*_*kin 9

(3)Collection myCollection = new ArrayList();

我通常使用这个.而只有当我需要列出方法,我将使用清单.与ArrayList相同.你总是可以切换到更"窄"的界面,但你不能切换到更"宽".

  • 这是人们做的事吗?通过这种推理,我应该到处使用Object.我可以想到很少有很多设计需要直接使用Collection接口.我将通过8个赞成票来假设至少有7个人这样做...... (17认同)
  • @OmarKooheji那让我开怀大笑:)但它仍然是一个有效的观点! (2认同)

Tru*_*oft 9

我认为使用(2)的人不知道Liskov替换原则依赖性倒置原则.或者他们真的必须使用ArrayList.


rau*_*udi 9

实际上有些情况下(2)不仅是首选而是强制性的,我很惊讶,没有人在这里提到这一点.

序列化!

如果你有一个可序列化的类,并且你希望它包含一个列表,那么你必须声明该字段是具体的和可序列化的类型,ArrayList因为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<>();
Run Code Online (Sandbox Code Playgroud)


i_a*_*ero 7

在以下两个中:

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
Run Code Online (Sandbox Code Playgroud)

首先通常是优选的.由于您将仅使用List接口中的方法,因此它为您提供了使用其他实现的自由,List例如LinkedList将来.因此它将您与特定实现分离.现在有两点值得一提:

  1. 我们应该始终编程接口.更多这里.
  2. 你几乎总是最终使用ArrayListLinkedList.更多这里.

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

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

此外,多久(我可以得到一个例子)情况实际上需要使用(1)over(2)

几乎总是你更喜欢选项(1).这是OOP中的经典设计模式,您始终尝试将代码从特定实现和程序与接口分离.


Xar*_*han 5

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

将来您可以更改List obj = new ArrayList<>List obj = new LinkList<>或其他实现List 接口的类型。

  • 你应该换一种说法:列表有方法,但它们没有实现你不能说“它没有方法”。 (5认同)

归档时间:

查看次数:

386077 次

最近记录:

6 年,3 月 前