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

What is the difference between List.of and Arrays.asList?

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

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

旧选项和新选项有什么区别?也就是这个有什么区别:

Arrays.asList(1, 2, 3);

还有这个:

List.of(1, 2, 3);

Arrays.asList returns 一个可变列表,而 List.of 返回的列表是 immutable:

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 允许空元素,而 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 returns 传递数组的视图,因此对数组的更改也会反映在列表中。 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]

总结一下List.ofArrays.asList

的区别
  1. List.of最适合数据集较少且不变的情况,而Arrays.asList最适合大数据集和动态数据集的情况。

  2. List.of 开销非常小 space 因为它具有基于字段的实现并且消耗更少的堆 space,无论是在固定开销还是在每个元素的基础。而 Arrays.asList 需要更多开销 space 因为在初始化时它会在堆中创建更多对象。

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

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

Arrays.asList and List.of

之间的区别

请参阅 Stuart Marks 的 JavaDocs and this talk(或它的早期版本)。

我将使用以下代码示例:

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

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

任何结构上改变List.of的尝试都会导致UnsupportedOperationException。这包括 addsetremove 等操作。但是,您可以更改列表中对象的内容(如果对象不是不可变的),因此列表不是 "completely immutable".

这与使用 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 版本上反序列化(没有 二进制兼容性 )。但是,您可以 de/serialize 和 JSON,例如。

身份

Arrays.asList 内部调用 new ArrayList,保证引用不等。

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

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 个或更少,则元素存储在专用(内部)class 的字段中。一个例子是存储 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 的方式存储在数组中。

时间和Space效率

基于字段(大小<2)的 List.of 实现在某些操作上执行速度稍快。例如,size() 可以 return 一个常量而无需获取数组长度,而 contains(E e) 不需要迭代开销。

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

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

这会创建 2 个列表以及其他开销。就 space 而言,您可以节省 UnmodifiableList 包装纸和一些便士。最终,HashSet 等值的节省更具说服力。


结论时间:当你想要一个不变的列表时使用List.of,当你想要一个可以更改的列表时使用Arrays.asList(如上所示)。

除了上述答案之外,List::ofArrays::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 Vs. List::of

Arrays.asList(1, 2, 3);

将创建一个 固定大小 列表

public static void main(String[] args) {
        List<Integer> asList = Arrays.asList(1, 2, 3, 4, 5);
        asList.add(6);    // java.lang.UnsupportedOperationException
        asList.remove(0); // java.lang.UnsupportedOperationException
        asList.set(0, 0); // allowed
}

List.of(1, 2, 3);

A Immutable(Java 9)/Unmodifiable(Java 11) List 将被创建:

public static void main(String[] args) {
    List<Integer> listOf = List.of(1, 2, 3, 4, 5);
    listOf.add(6);    // java.lang.UnsupportedOperationException
    listOf.remove(0); // java.lang.UnsupportedOperationException
    listOf.set(0, 0); // java.lang.UnsupportedOperationException
}