List.of和Arrays.asList有什么区别?


117

Java 9为列表引入了新的工厂方法List.of

List<String> strings = List.of("first", "second");

上一个选项和新选项之间有什么区别?也就是说,这之间有什么区别:

Arrays.asList(1, 2, 3);

还有这个:

List.of(1, 2, 3);

1
另请参阅Stuart“ Beaker” Marks的演讲
user1803551

20
@ user1803551尽管我理解您的无奈,但这种推理可能会树立一个真正令人讨厌的先例。这里的许多问题都有一个“明确说明”的答案(取决于人们对此的定义)。我敦促您将此讨论带到meta上,但我很确定这样的讨论应该已经存在(并且希望有人可以找到它并将其链接起来:-)
Dimitris Fasarakis Hilliard

4
@ user1803551 Javadocs没有提到这两种方法的实现细节之间的区别(例如空间消耗或性能)。我想人们也想知道这些细节。
ZhekaKozlov

5
@ZhekaKozlov接受并获得超级支持的答案也没有。关于公认的标准,这告诉您什么?它甚至比文档中的信息更少(序列化,身份,排序)。如果有的话,请向OpenJDK提交请求以添加该信息。
user1803551 17-10-6

3
这个问题正在meta上讨论。
Dimitris Fasarakis Hilliard '10

Answers:


173

Arrays.asList返回可变的列表,而所返回的列表List.of不可变的

List<Integer> list = Arrays.asList(1, 2, null);
list.set(1, 10); // OK

List<Integer> list = List.of(1, 2, 3);
list.set(1, 10); // Fails with UnsupportedOperationException

Arrays.asList允许null元素,而List.of不允许:

List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException

contains 行为与null不同:

List<Integer> list = Arrays.asList(1, 2, 3);
list.contains(null); // Returns false

List<Integer> list = List.of(1, 2, 3);
list.contains(null); // Fails with NullPointerException

Arrays.asList返回所传递数组的视图,因此对数组的更改也将反映在列表中。对于List.of这是不正确的:

Integer[] array = {1,2,3};
List<Integer> list = Arrays.asList(array);
array[1] = 10;
System.out.println(list); // Prints [1, 10, 3]

Integer[] array = {1,2,3};
List<Integer> list = List.of(array);
array[1] = 10;
System.out.println(list); // Prints [1, 2, 3]

22
对于列表而言,基于其构造方式而表现出不同的行为对我来说似乎不是面向对象的。也许List.of返回了ImmutableList类型,这很有意义。这是一个非常泄漏的抽象。
桑迪·查普曼

5
我不是Java开发人员,因此请随意观察。行为上的差异可能有充分的理由,但是如果我有一个像示例一样返回List <Integer>的方法,那么如果我检查它,接口将不足以让我知道是否会遇到运行时异常为空。同样,如果该检查发生在其他地方,则该方法实现的更改可能会影响与我的方法的调用站点相距较远的代码。@Nicolai
桑迪·查普曼

8
@SandyChapman对于某些(或大多数?)来说,这可能是意外行为,但已记录为行为。来自List.contains(Object o)的javadoc:“抛出NullPointerException-如果指定的元素为null,并且此列表不允许null元素(可选)”。或者从界面的冗长介绍中,很少有人读到:“某些收集实现对它们可能包含的元素有限制”
Aaron

11
@Aaron至少它是有据可查的泄漏抽象:)
Sandy Chapman

6
@Sandy Chapman:List.of 确实返回了某种ImmutableList类型,其实际名称只是一个非公开的实现细节。如果它是公开的并且有人List再次将其抛弃,那么区别在哪里?与的区别在哪里Arrays.asList,它返回一个非公共的List实现,在尝试add或时抛出异常remove,或者返回的列表Collections.unmodifiableList根本不允许任何修改?这都是关于List界面中指定的合同的。自Java 1.2以来,带有可选方法的Collections接口始终是不纯正的OOP…
Holger

31

Arrays.asList和之间的区别List.of

请参阅JavaDocs和Stuart Marks的演讲(或以前的版本)。

我将使用以下代码示例:

List<Integer> listOf = List.of(...);
List<Integer> asList = Arrays.asList(...);
List<Integer> unmodif = Collections.unmodifiableList(asList);

结构不变性(或:不可修改性)

任何结构上的改变List.of都会导致损失UnsupportedOperationException。其中包括添加设置删除之类的操作。但是,您可以更改列表中对象的内容(如果对象不是不可变的),因此列表不是“完全不可变的”。

这与使用创建的不可修改列表的命运相同Collections.unmodifiableList。仅此列表是原始列表的视图,因此,如果您更改原始列表,它可以更改。

Arrays.asList不是完全不变的,它对没有限制set

listOf.set(1, "a");  // UnsupportedOperationException
unmodif.set(1, "a"); // UnsupportedOperationException
asList.set(1, "a");  // modified unmodif! unmodif is not truly unmodifiable

同样,更改后备数组(如果按住)将更改列表。

结构不变性带来了与防御性编码,并发性和安全性相关的许多副作用,这些副作用超出了此答案的范围。

空敌

List.of以及Java 1.5之后的任何集合都不允许null作为元素。尝试将其null作为元素或什至通过传递将导致NullPointerException

由于Arrays.asList是1.2(集合框架)的集合,因此允许nulls。

listOf.contains(null);  // NullPointerException
unmodif.contains(null); // allowed
asList.contains(null);  // allowed

序列化表格

由于List.of已在Java 9中引入,并且由此方法创建的列表具有其自己的(二进制)序列化形式,因此无法在较早的JDK版本上进行反序列化(无二进制兼容性)。但是,例如,您可以使用JSON进行反序列化。

身分识别

Arrays.asList内部调用new ArrayList,以确保参考不平等。

List.of取决于内部实现。返回的实例可以具有引用相等性,但是由于不能保证您不能依赖它。

asList1 == asList2; // false
listOf1 == listOf2; // true or false

值得一提的是,如果列表List.equals包含相同顺序的相同元素,则列表是相等的(通过),而不管它们是如何创建的或支持的操作。

asList.equals(listOf); // true i.f.f. same elements in same order

实施(警告:详细信息可能会因版本而异)

如果列表中的元素数量List.of为2个或更少,则这些元素将存储在专用(内部)类的字段中。一个示例是存储2个元素的列表(部分来源):

static final class List2<E> extends AbstractImmutableList<E> {
    private final E e0;
    private final E e1;

    List2(E e0, E e1) {
        this.e0 = Objects.requireNonNull(e0);
        this.e1 = Objects.requireNonNull(e1);
    }
}

否则,它们将以类似于的方式存储在数组中Arrays.asList

时空效率

List.of其是基于场的(大小<2)实施方式中的一些操作速度稍快执行。作为示例,size()可以在不获取数组长度的情况下返回常量,并且contains(E e)不需要迭代开销。

通过构造一个不可修改的列表List.of也更快。将上面的构造函数与2个引用分配(甚至是任意数量的元素的引用分配)进行比较,以得出

Collections.unmodifiableList(Arrays.asList(...));

这将创建2个列表以及其他开销。就空间而言,您可以节省UnmodifiableList包装纸和几分钱。最终,HashSet同等成本的节省更具说服力。


结论时间:List.of当您希望列表不变时,以及Arrays.asList想要列表可以更改时使用(如上所示)。


1
对于想知道为什么存在这个答案的人,请参阅
user1803551

3
Arrays.asList不是完全可变的。asList.add(1);抛出一个UnsupportedOperationException
mapeters

“零敌对”是表达它的好方法。我几乎无法使用List.of任何时候人们都想打电话contains给NullPointerException感到惊讶。
Noumenon

14

让我们总结一下List.ofArrays.asList之间的区别

  1. List.of数据集较少且不变时,Arrays.asList最好使用此方法;而动态数据集较大时,最好使用。

  2. List.of占用很少的开销空间,因为它具有基于字段的实现,并且在固定开销和每个元素的基础上都消耗较少的堆空间。而Arrays.asList需要更多开销的空间,因为当初始化它会在堆更多的对象。

  3. 返回的集合List.of是不可变的,因此是线程安全的,而返回的集合Arrays.asList是可变的,而不是线程安全的。(不可变集合实例通常比可变实例消耗更少的内存。)

  4. List.of不允许使用null元素,而Arrays.asList允许使用null元素。


2
“不可变集合实例通常比可变实例消耗更少的内存。” -真的吗?您是否愿意对此进行详细说明-是因为可以安全地共享它们,还是要以某种方式更有效地实现实例本身?
绿巨人

1
@Hulk回答者对空间效率是正确的。参见Stuart Marks的演讲:youtu.be/q6zF3vf114M?
t=49m48s

2
@ZhekaKozlov一般来说,这似乎是正确的,但我非常怀疑在谈论Arrays.asListvs 时是否正确List.of,因为前者实际上只是数组的包装。至少OpenJDK的实现似乎开销很小。实际上,List.of需要复制任何传入的数组的副本,因此,除非该数组本身即将被GC淘汰,否则似乎List.of占用的内存空间要大得多。
克里斯·海斯

4
@ChrisHayes至少List.of(x)List.of(x, y)更有效,因为他们根本就不分配阵列
ZhekaKozlov

2
@Hulk:不要忘记List.of每次都不需要方法返回新列表。这些列表具有未指定的标识,因此可能在JVM级别上进行了缓存,重复数据删除或标量处理。如果不在此版本中,则可能在下一个版本中。合同允许的。相反,Array.asList取决于您要传递的数组的标识,因为结果列表是数组上的可变视图,反映了所有双向更改。
Holger

2

除上述答案外,还有某些操作,两者List::of和都Arrays::asList不同:

+----------------------+---------------+----------+----------------+---------------------+
|      Operations      | SINGLETONLIST | LIST::OF | ARRAYS::ASLIST | JAVA.UTIL.ARRAYLIST |
+----------------------+---------------+----------+----------------+---------------------+
|          add         |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        addAll        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         clear        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        remove        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       removeAll      |       ❗️       |        |        ❗️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       retainAll      |       ❗️       |       |        ❗️        |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|      replaceAll      |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|          set         |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         sort         |       ✔️       |        |        ✔️      |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|  remove on iterator  |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
| set on list-iterator |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
  1. ✔️表示该方法受支持
  2. ❌表示调用此方法将引发UnsupportedOperationException
  3. ❗️表示仅当方法的参数不会引起突变时才支持该方法,例如Collections.singletonList(“ foo”)。retainAll(“ foo”)可以,但是Collections.singletonList(“ foo”)。retainAll(“ bar” )引发UnsupportedOperationException

有关Collections :: singletonList的更多信息。列表


1
Java考试答案
povis
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.