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

95 java list java-9

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

List<String> strings = List.of("first", "second");
Run Code Online (Sandbox Code Playgroud)

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

Arrays.asList(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

还有这个:

List.of(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

Zhe*_*lov 145

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

  • 对于一个列表,根据它的构造方式表现不同对我来说似乎不太面向对象.也许如果List.of返回一个ImmutableList类型,这将是有道理的.这是一个非常漏洞的抽象. (18认同)
  • @Aaron至少它是一个记录良好的漏洞抽象:) (9认同)
  • @SandyChapman这可能是某些(或大多数?)的意外行为,但它是记录在案的行为.来自[`List.contains(Object o)`的javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/List.html#contains-java.lang.Object - ):"抛出[...] NullPointerException - 如果指定的元素为null并且此列表不允许null元素(可选)".或者从界面的长篇介绍中读到:"有些集合实现对它们可能包含的元素有限制" (7认同)
  • @Sandy Chapman:`List.of`*确实*返回一些`ImmutableList`类型,它的实际名称只是一个非公开的实现细节.如果它是公开的并且有人再次将它投入"List",那么区别在哪里?与返回非公共`List`实现的`Arrays.asList`的不同之处在于,它在尝试`add`或`remove`时抛出异常,或者由`Collections.unmodifiableList`返回的列表不允许修改什么?这都是关于`List`接口中指定的合同.自Java 1.2以来,带有可选方法的Collections接口总是不纯的OOP ... (5认同)
  • 我不是Java开发人员,所以把它作为一个随意的观察.行为有可能是一个很好的理由,但是如果我有一个返回List <Integer>的方法,那么接口就不足以让我知道如果我检查它是否会得到运行时异常对于空值.同样,如果该检查在其他地方发生,那么该方法实现的更改可能会影响远离我的方法的调用站点的代码.@Nicolai (4认同)
  • @Holger 我相信你们都在谈论同一个问题。也就是说,`java.util.Collection` 及其后代有太多的责任。更纯的 API(例如 Eclipse Collections)将为不可变集合提供细粒度的接口。现在,我同意这是一个相当老的问题,但即使在新方法中它也会再次表现出来。 (3认同)
  • Arrays.asList() 是半可变的。正如已经指出的,返回的列表是传递的数组参数的视图。是的,变化虽然过去了。然而,另一个结果是返回的 List 无法像人们期望的那样动态地增加或减少其大小。您无法向其中添加新元素。 (2认同)

use*_*551 25

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

请参阅JavaDocs和Stuart Marks(或其先前版本)的演讲.

我将使用以下代码示例:

List<Integer> listOf = List.of(...);
List<Integer> asList = Arrays.asList(...);
List<Integer> unmodif = Collections.unmodifiableList(asList);
Run Code Online (Sandbox Code Playgroud)

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

任何结构改变的尝试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
Run Code Online (Sandbox Code Playgroud)

同样,更改后备阵列(如果您持有)将更改列表.

结构不变性伴随着许多与防御性编码,并发性和安全性相关的副作用,超出了本答案的范围.

无足轻重的敌意

List.of自Java 1.5以来的任何集合都不允许null作为元素.尝试null作为元素或甚至查找传递将导致a NullPointerException.

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

listOf.contains(null);  // NullPointerException
unmodif.contains(null); // allowed
asList.contains(null);  // allowed
Run Code Online (Sandbox Code Playgroud)

序列化表格

由于List.of已经在Java 9中引入并且由此方法创建的列表具有它们自己的(二进制)序列化形式,因此它们不能在早期的JDK版本上反序列化(没有二进制兼容性).但是,您可以使用JSON进行de/serialize.

身分

Arrays.asList内部调用new ArrayList,保证参考不等式.

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

asList1 == asList2; // false
listOf1 == listOf2; // true or false
Run Code Online (Sandbox Code Playgroud)

值得一提的是,如果列表List.equals包含相同顺序的相同元素,则它们是相同的(via ),无论它们是如何创建的,还是它们支持的操作.

asList.equals(listOf); // true i.f.f. same elements in same order
Run Code Online (Sandbox Code Playgroud)

实施(警告:细节可以更改版本)

如果列表中的元素数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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

时空效率

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

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

Collections.unmodifiableList(Arrays.asList(...));
Run Code Online (Sandbox Code Playgroud)

这会创建2个列表以及其他开销.在空间方面,您可以保存UnmodifiableList包装器和一些便士.最终,HashSet相当于节省的成本更具说服力.


结束时间:List.of当您想要一个不会更改Arrays.asList的列表以及何时需要可以更改的列表时使用(如上所示).

  • `Arrays.asList`不是完全可变的.`asList.add(1);`抛出`UnsupportedOperationException`. (3认同)
  • 对于想知道为什么这个答案存在的人,请参阅 [this](https://meta.stackoverflow.com/a/357505/1803551)。 (2认同)

Moh*_*agi 14

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

  1. List.of当数据集较少且不变时,Arrays.asList可以最好地使用,而在大型和动态数据集的情况下可以最佳地使用.

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

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

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

  • @ChrisHayes至少`List.of(x)`和`List.of(x,y)`效率更高,因为它们根本不分配数组 (4认同)
  • "不可变的集合实例通常比可变的集合实例消耗更少的内存." - 真的吗?你是否愿意详细说明一下 - 你的意思是因为它们可以安全地共享,还是你的意思是它们可以以某种方式更有效地实现它们自己的实例? (2认同)
  • @Hulk 回答者对空间效率的看法是正确的。参见 Stuart Marks 的演讲:https://youtu.be/q6zF3vf114M?t=49m48s (2认同)
  • @ZhekaKozlov这看起来似乎是正确的,但我非常怀疑,当谈论`Arrays.asList`和`List.of`时,它是真的,因为前者实际上只是一个数组的包装器.至少[OpenJDK的实现](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Arrays.java#3361)看起来非常小高架.实际上,`List.of`需要复制传入的任何数组,因此除非数组本身很快就会被GC化,否则看起来`List.of`的内存占用量会大得多. (2认同)
  • 不过,我同意 - 我没有考虑到在与一般可变列表进行比较时,这种说法是正确的,因为它们不需要增长能力。 (2认同)
  • @Hulk:不要忘记每次都需要`List.of`方法返回新列表.这些列表具有未指定的标识,因此可以在JVM级别上处理缓存或重复数据删除或标量化.如果不是在这个版本中,那么也许在下一个版本中.这是合同允许的.相反,`Array.asList`取决于您传入的数组的标识,因为结果列表是数组上的可变视图,反映了所有双向更改. (2认同)

Vis*_*tna 10

除了上述答案之外,还有一些操作是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 |       ?      |     ?  |        ??       |          ??          |
+----------------------+---------------+----------+----------------+---------------------+
Run Code Online (Sandbox Code Playgroud)
  1. ?? 表示支持该方法
  2. ? 意味着调用此方法将抛出 UnsupportedOperationException
  3. ?? 表示仅当方法的参数不会导致突变时才支持该方法,例如 Collections.singletonList("foo").retainAll("foo") 可以,但 Collections.singletonList("foo").retainAll("bar")抛出 UnsupportedOperationException

更多关于Collections::singletonList 与。清单


小智 10

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
}
Run Code Online (Sandbox Code Playgroud)

列表 (1, 2, 3);

将创建一个不可变(Java 9)/不可修改(Java 11) 列表:

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
}
Run Code Online (Sandbox Code Playgroud)