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

Java 9引入了新的Collection Factory方法List.of

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

那么,以前的和新的select有什么区别呢? 那是什么区别呢:

 Arrays.asList(1, 2, 3); 

和这个:

 List.of(1, 2, 3); 

Arrays.asList返回一个可变列表, Arrays.asList返回的列表是不可变的:

 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 

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

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

contains方法与空值performance不同:

 List<Integer> list = Arrays.asList(1, 2, 3); list.contains(null); // Return false List<Integer> list = List.of(1, 2, 3); list.contains(null); // Throws 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] 

我们来总结List.ofArrays.asList之间的区别

  1. 当数据集较less且不变时, List.of可以得到最好的使用,而Arrays.asList可以在大型和dynamic数据集的情况下使用得最好。

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

  3. List.of返回的List.of是不可变的,因此线程安全,而由Arrays.asList返回的Arrays.asList是可变的,而不是线程安全的。 (不可变集合实例通常消耗的内存比它们的可变对象less得多。)

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

Arrays.asListList.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创build的不可修改列表的命运是一样的。 只有这个列表是原始列表的一个视图 ,所以如果你改变原始列表,它可以改变。

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

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

同样,更改后备数组(如果您持有它)将更改列表。

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

没有敌意

自Java 1.5以来, List.of和任何集合都不允许null作为元素。 试图传递null作为一个元素,甚至查找将导致NullPointerException

由于Arrays.asList是一个来自1.2(集合框架)的集合,它允许null

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

序列化的forms

由于List.of已经在Java 9中引入,并且由此方法创build的列表具有自己的(二进制)序列化forms,所以在早期的JDK版本(无二进制兼容性 )上不能对其进行反序列化。 但是,例如,您可以使用JSON进行反编译/序列化。

身分

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

List.of取决于内部的实现。 返回的实例可以有引用的平等,但是因为不能保证你不能依赖它。

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

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

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

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

如果List.of列表中的元素数量是2或更less,则元素存储在专用(内部)类的字段中。 一个例子是存储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类似的方式存储在一个数组中。

时间和空间效率

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

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

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

这会创build2个列表以及其他开销。 在空间方面,你保存UnmodifiableList包装加上一些便士。 最终,相当于HashSet的节省更加令人信服。


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