В чем разница между List.of и Arrays.asList?
Java 9 представила новые фабричные методы для списков, List.of
:
List<String> strings = List.of("first", "second");
В чем разница между предыдущим и новым вариантом? То есть какая разница между этим:
Arrays.asList(1, 2, 3);
и это:
List.of(1, 2, 3);
Ответы
Ответ 1
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
разрешает нулевые элементы, а List.of
:
List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException
contains
ведет себя по-разному с нулями:
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]
Ответ 2
Смотрите JavaDocs и этот talk от Стюарта Маркса (или предыдущие версии).
Я буду использовать следующие примеры кода:
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
Аналогично, изменение массива поддержки (если вы его удерживаете) изменит список.
Структурная неизменность приходит со многими побочными эффектами, связанными с защитным кодированием, concurrency и безопасностью, которые выходят за рамки этого ответа.
Неверная враждебность
List.of
, и любая коллекция, поскольку Java 1.5 не позволяет null
как элемент. Попытка передать null
как элемент или даже поиск приведет к NullPointerException
.
Так как Arrays.asList
представляет собой набор из 1.2 (Framework Collections), он позволяет null
s.
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
также выполняется быстрее. Сравните вышеуказанный конструктор с двумя ссылочными присвоениями (и даже для произвольного количества элементов) до
Collections.unmodifiableList(Arrays.asList(...));
который создает 2 списка плюс другие накладные расходы. Что касается пространства, вы сохраняете обертку UnmodifiableList
плюс несколько копеек. В конечном счете экономия в эквиваленте HashSet
более убедительна.
Заключение: используйте List.of
, если вам нужен список, который не изменяется, и Arrays.asList
, если вам нужен список, который может измениться (как показано выше).
Ответ 3
Обобщите различия между List.of и Arrays.asList
-
List.of
лучше всего использовать, когда набор данных меньше и неизменен, а Arrays.asList
может использоваться наилучшим образом в случае большого и динамического набора данных.
-
List.of
занимает очень мало места накладных расходов, потому что он имеет реализацию на основе полей и потребляет меньше места кучи, как с точки зрения фиксированных накладных расходов, так и на основе каждого элемента. в то время как Arrays.asList
занимает больше служебного пространства, потому что при инициализации он создает больше объектов в куче.
-
Сбор, возвращенный List.of
, является неизменным и, следовательно, поточно-безопасным, тогда как Collection, возвращаемый Arrays.asList
, является изменяемым и не является потокобезопасным.
(Неизменяемые экземпляры коллекции обычно потребляют гораздо меньше памяти, чем их изменяемые копии.)
-
List.of
не разрешает нулевые элементы, а Arrays.asList
допускает нулевые элементы.
Ответ 4
Помимо приведенных выше ответов, существуют определенные операции, в которых отличаются 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 | ❌ | ❌ | ✔️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
- ✔️ означает, что метод поддерживается
- ❌ означает, что вызов этого метода вызовет
UnsupportedOperationException
- ❗️ означает, что метод поддерживается, только если аргументы метода
не вызывает мутации, например
Collections.singletonList("foo"). RetainAll ("foo") в порядке, но
Collections.singletonList("foo"). RetainAll ("bar") создает
UnsupportedOperationException
Подробнее о коллекциях :: singletonList Vs. Список