Qual é a diferença entre List.of e Arrays.asList?

117

Java 9 introduziu novos métodos de fábrica para listas List.of:

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

Qual é a diferença entre a opção anterior e a nova? Ou seja, qual é a diferença entre isso:

Arrays.asList(1, 2, 3);

e isto:

List.of(1, 2, 3);
ZhekaKozlov
fonte
1
Veja também esta palestra de Stuart "Beaker" Marks.
user1803551
20
@ user1803551 Embora eu entenda sua frustração, esse raciocínio pode abrir um precedente realmente indesejado. Muitas perguntas aqui têm uma resposta 'claramente declarada' (dependendo de como alguém a define). Recomendo que você traga essa discussão para meta, mas tenho certeza de que essa discussão já deve existir (e espero que alguém possa encontrá-la e vinculá-la :-)
Dimitris Fasarakis Hilliard
4
@ user1803551 Javadocs não mencionam a diferença entre os detalhes de implementação desses dois métodos (como consumo de espaço ou desempenho). Acho que as pessoas também gostariam de saber esses detalhes.
ZhekaKozlov,
5
@ZhekaKozlov A resposta aceita e supervotada também não. O que isso diz sobre os padrões aceitos? Ele ainda tem menos informações do que nos documentos (serialização, identidade, ordenação). Se houver alguma coisa, envie uma solicitação ao OpenJDK para adicionar essas informações.
user1803551
3
Esta questão está sendo discutida no meta .
Dimitris Fasarakis Hilliard

Respostas:

173

Arrays.asListretorna uma lista mutável enquanto a lista retornada por List.ofé imutável :

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.asListpermite elementos nulos enquanto List.ofnão:

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

contains se comporta de maneira diferente com nulos:

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.asListretorna uma visão do array passado, então as mudanças no array serão refletidas na lista também. Pois List.ofisso não é verdade:

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]
ZhekaKozlov
fonte
22
Para uma lista se comportar de maneira diferente com base em como ela é construída, não me parece muito orientada a objetos. Talvez se List.of retornasse um tipo ImmutableList, isso faria sentido. Esta é uma abstração muito furada aqui.
Sandy Chapman,
5
Não sou um desenvolvedor Java, então tome isso como uma observação casual. É possível que haja uma boa razão para o comportamento ser diferente, mas se eu tivesse um método retornando um List <Integer> como o exemplo, a interface não seria suficiente para eu saber se terei uma exceção de tempo de execução se eu verificar para nulos. Da mesma forma, uma mudança na implementação de métodos pode afetar o código distante do site de chamada do meu método se essa verificação acontecer em outro lugar. @Nicolai
Sandy Chapman,
8
@SandyChapman este pode ser um comportamento inesperado para alguns (ou para a maioria?), Mas é um comportamento documentado. Do List.contains(Object o)javadoc de : "Lança [...] NullPointerException - se o elemento especificado for nulo e esta lista não permitir elementos nulos (opcional)". Ou na introdução longa da interface que poucos lêem: "Algumas implementações de coleção têm restrições sobre os elementos que podem conter"
Aaron
11
@Aaron bem, pelo menos é uma abstração que vaza bem documentada :)
Sandy Chapman
6
Chapman @Sandy: List.of não retornar algum ImmutableListtipo, seu nome real é apenas um detalhe de implementação não-pública. Se fosse público e alguém o publicasse Listnovamente, qual seria a diferença? Onde está a diferença para Arrays.asList, que retorna uma Listimplementação não pública , que lança uma exceção ao tentar addou remove, ou a lista retornada por Collections.unmodifiableListque não permite nenhuma modificação? É tudo sobre contratos especificados na Listinterface. As interfaces de coleções com métodos opcionais sempre foram OOP impuras desde Java 1.2 ...
Holger
31

As diferenças entre Arrays.asListeList.of

Veja os JavaDocs e esta palestra de Stuart Marks (ou versões anteriores).

Usarei o seguinte para os exemplos de código:

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

Imutabilidade estrutural (Ou: inalterabilidade)

Qualquer tentativa de mudança estruturalList.of resultará em um UnsupportedOperationException. Isso inclui operações como adicionar , definir e remover . Você pode, entretanto, alterar o conteúdo dos objetos na lista (se os objetos não forem imutáveis), para que a lista não seja "completamente imutável".

Este é o mesmo destino para listas não modificáveis ​​criadas com Collections.unmodifiableList. Apenas esta lista é uma visualização da lista original, portanto, ela pode mudar se você alterar a lista original.

Arrays.asListnão é completamente imutável, não tem uma restrição sobre set.

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

Da mesma forma, alterar a matriz de apoio (se você a mantiver) mudará a lista.

A imutabilidade estrutural vem com muitos efeitos colaterais relacionados à codificação defensiva, simultaneidade e segurança que estão além do escopo desta resposta.

Hostilidade nula

List.ofe qualquer coleção desde Java 1.5 não permite nullcomo um elemento. A tentativa de passar nullcomo um elemento ou mesmo uma pesquisa resultará em um NullPointerException.

Como Arrays.asListé uma coleção de 1.2 (o Framework de coleções), ele permite nulls.

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

Formulário serializado

Como List.offoi introduzido no Java 9 e as listas criadas por esse método têm sua própria forma serializada (binária), elas não podem ser desserializadas em versões anteriores do JDK (sem compatibilidade binária ). No entanto, você pode cancelar / serializar com JSON, por exemplo.

Identidade

Arrays.asListchamadas internamente new ArrayList, o que garante a desigualdade de referência.

List.ofdepende da implementação interna. As instâncias retornadas podem ter igualdade de referência, mas como isso não é garantido, você não pode confiar nela.

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

Vale ressaltar que as listas são iguais (via List.equals) se contiverem os mesmos elementos na mesma ordem, independentemente de como foram criadas ou das operações que suportam.

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

Implementação (aviso: os detalhes podem mudar nas versões)

Se o número de elementos na lista de List.offor 2 ou menos, os elementos serão armazenados em campos de uma classe especializada (interna). Um exemplo é a lista que armazena 2 elementos (fonte parcial):

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);
    }
}

Caso contrário, eles são armazenados em uma matriz de maneira semelhante a Arrays.asList.

Eficiência de tempo e espaço

As List.ofimplementações baseadas em campo (tamanho <2) têm um desempenho ligeiramente mais rápido em algumas operações. Como exemplos, size()pode retornar uma constante sem buscar o comprimento do array e contains(E e)não requer sobrecarga de iteração.

Construir uma lista não modificável via List.oftambém é mais rápido. Compare o construtor acima com 2 atribuições de referência (e até mesmo aquele para quantidade arbitrária de elementos) para

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

que cria 2 listas mais outra sobrecarga. Em termos de espaço, você economiza o UnmodifiableListinvólucro e alguns centavos. Em última análise, a economia no HashSetequivalente é mais convincente.


Tempo de conclusão: use List.ofquando quiser uma lista que não muda e Arrays.asListquando quiser uma lista que pode mudar (como mostrado acima).

user1803551
fonte
1
Para as pessoas que estão se perguntando por que essa resposta existe, veja isto .
user1803551
3
Arrays.asListnão é totalmente mutável. asList.add(1);joga um UnsupportedOperationException.
mapeters
"Null hostile" é uma ótima maneira de colocar isso. Eu praticamente não posso usar List.ofnenhum momento que as pessoas possam querer chamar containse não ficar surpreso com um NullPointerException.
Noumenon
14

Vamos resumir as diferenças entre List.of e Arrays.asList

  1. List.ofpode ser usado melhor quando o conjunto de dados é menor e inalterado, enquanto Arrays.asListpode ser usado melhor no caso de um conjunto de dados grande e dinâmico.

  2. List.ofocupa muito menos espaço de sobrecarga porque tem implementação baseada em campo e consome menos espaço de heap, tanto em termos de sobrecarga fixa quanto em uma base por elemento. enquanto Arrays.asListocupa mais espaço de overhead porque durante a inicialização cria mais objetos no heap.

  3. A coleção retornada por List.ofé imutável e, portanto, segura para threads, enquanto a coleção retornada por Arrays.asListé mutável e não segura para threads. (Instâncias de coleção imutáveis ​​geralmente consomem muito menos memória do que suas contrapartes mutáveis.)

  4. List.ofnão permite elementos nulos enquanto Arrays.asListpermite elementos nulos .

Mohit Tyagi
fonte
2
"Instâncias de coleção imutáveis ​​geralmente consomem muito menos memória do que suas contrapartes mutáveis." - Sério? Você gostaria de elaborar um pouco sobre isso - você quer dizer porque eles podem ser compartilhados com segurança, ou quer dizer que as próprias instâncias podem ser implementadas de forma mais eficiente de alguma forma?
Hulk
1
@Hulk O respondente está certo sobre a eficiência do espaço. Veja a palestra de Stuart Marks: youtu.be/q6zF3vf114M?t=49m48s
ZhekaKozlov
2
@ZhekaKozlov Isso parece ser verdade em geral, mas estou muito cético de que seja verdade quando falamos sobre Arrays.asListversus List.of, visto que o primeiro é literalmente apenas um invólucro em torno de um array. Pelo menos a implementação do OpenJDK parece ter uma sobrecarga extremamente pequena. Na verdade, List.ofseria necessário fazer cópias de qualquer array passado, então, a menos que o próprio array seja submetido a GC em breve, parece que List.oftem uma pegada de memória significativamente maior.
Chris Hayes,
4
@ChrisHayes Pelo menos List.of(x)e List.of(x, y)são mais eficientes porque não alocam matrizes
ZhekaKozlov,
2
@Hulk: não se esqueça de que os List.ofmétodos não precisam retornar novas listas a cada vez. Essas listas têm uma identidade não especificada, portanto, pode haver armazenamento em cache, deduplicação ou escalarização tratada no nível da JVM. Se não nesta versão, talvez na próxima. É permitido pelo contrato. Em contraste, Array.asListdepende da identidade do array que você está passando, já que a lista resultante é uma visualização mutável no array, refletindo todas as mudanças bidirecionais.
Holger,
2

Além das respostas acima, existem certas operações nas quais List::ofe Arrays::asListdiferem:

+----------------------+---------------+----------+----------------+---------------------+
|      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. ✔️ significa que o método é compatível
  2. ❌ significa que chamar este método lançará uma exceção UnsupportedOperationException
  3. ❗️ significa que o método é suportado apenas se os argumentos do método não causam uma mutação, por exemplo, Collections.singletonList ("foo"). RetêmAll ("foo") está OK, mas Collections.singletonList ("foo"). RetêmAll ("bar") ) lança uma UnsupportedOperationException

Mais sobre Coleções :: singletonList vs. Lista de

Vishwa Ratna
fonte
1
resposta para exame java
povis