Lista de tipos vs lista de tipos em Java

(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

eu entendo que com (1), implementações da interface List podem ser trocadas. Parece que (1) é tipicamente usado em uma aplicação independente da necessidade (eu sempre uso isso).

Estou me perguntando se alguém usa (2)?

também, com que frequência (e posso por favor obter um exemplo) a situação realmente requer o uso (1) sobre (2) (ou seja, onde (2) não seria suficiente..de lado. codificação das interfaces e melhor práticas etc.)

Author: nazar_art, 2010-02-17

15 answers

Quase sempre o primeiro é preferido ao segundo. O primeiro tem a vantagem de que a implementação do {[[0]} pode mudar (para um LinkedList por exemplo), sem afetar o resto do Código. Esta será uma tarefa difícil para fazer com um ArrayList, não só porque você vai precisar mudar ArrayList para LinkedList em todos os lugares, mas também porque você pode ter usado ArrayList métodos específicos.

Você pode ler sobre List implementações aqui. Pode começar com um ArrayList, mas logo depois descobrir que outra implementação é mais apropriada.

 378
Author: kgiannakakis, 2014-05-21 00:43:14
Estou me perguntando se alguém usa (2)?
Sim. Mas raramente por uma boa razão (IMO).

E as pessoas queimam-se porque usaram ArrayList quando deviam ter usado List:

  • Métodos de utilidade como Collections.singletonList(...) ou Arrays.asList(...) não devolvam um ArrayList.

  • Os Métodos na API List não garantem devolver uma lista do mesmo tipo.

Por exemplo, alguém a ser queimado, em https://stackoverflow.com/a/1481123/139985 o cartaz tinha problemas com "cortar" porque ArrayList.sublist(...) não devolve um ArrayList ... e ele desenhou seu código para usar ArrayList como o tipo de todas as variáveis de sua lista. Ele acabou "resolvendo" o problema copiando a sub-lista para um novo ArrayList.

O argumento de que precisa de saber como o List se comporta é amplamente abordado usando a interface de marcação RandomAccess. Sim, é um pouco desajeitado, mas a alternativa é pior.

Também, com que frequência é que a situação realmente requer o uso de (1) sobre (2) (ou seja, onde (2) não seria suficiente..além da "codificação às interfaces" e das melhores práticas, etc.)

A parte "quantas vezes" da pergunta é objectivamente irrespondível.

(e posso por favor obter um exemplo)

Ocasionalmente, a aplicação pode exigir que utilize métodos na API ArrayList que não sejam na APIList. Por exemplo, ensureCapacity(int), trimToSize() ou removeRange(int, int). (E o último só surgirá se você tiver criado um subtipo de ArrayList que declara que o método é public.)

Essa é a única razão sonora para codificar para a classe e não para a interface, IMO.

(é teoricamente possível que consiga uma ligeira melhoria no desempenho ... sob certas circunstâncias ... em algumas plataformas ... mas a não ser que precises mesmo dos últimos 0,05%, não vale a pena fazer isto. Esta não é uma boa razão, IMO.)


Você não pode escrever um código eficiente se você não sabe se o acesso aleatório é eficiente ou não.

É um ponto válido. No entanto, Java oferece melhores maneiras de lidar com isso; por exemplo
public <T extends List & RandomAccess> void test(T list) {
    // do stuff
}

Se você chamar isso com uma lista que não implementa RandomAccess você vai obter um erro de compilação.

Também podes testar dinamicamente ... usando instanceof ... se escrever estática é muito estranho. E você poderia até mesmo escrever seu código para usar diferentes algoritmos (dinamicamente) dependendo se uma lista suportava ou não o acesso aleatório.

Note que ArrayList não é a única classe de listas que implementa RandomAccess. Outros incluem CopyOnWriteList, Stack e Vector

Já vi pessoas fazerem o mesmo argumento sobre Serializable (Porque List não o aplica) ... mas a abordagem acima resolve também este problema. (To the extent that it is solvable at all using runtime types. Um ArrayList falhará a serialização se algum elemento não for serializável.)

 99
Author: Stephen C, 2018-09-06 01:30:52

Por exemplo, você pode decidir que LinkedList é a melhor escolha para a sua aplicação, mas depois decidir ArrayList pode ser uma escolha melhor por razões de desempenho.

Utilizar:

List list = new ArrayList(100); // will be better also to set the initial capacity of a collection 

Em vez de:

ArrayList list = new ArrayList();

Para referência:

enter image description here

(publicado principalmente para o diagrama de colecções)

 35
Author: Maxim Shoustin, 2014-09-09 15:12:05

É considerado um bom estilo para armazenar uma referência a HashSet ou TreeSet numa variável de tipo.

Set<String> names = new HashSet<String>();

Desta forma, você tem que mudar apenas uma linha se você decidir usar um TreeSet em vez disso.

Além disso, os métodos que funcionam em conjuntos devem especificar os parâmetros do Tipo Conjunto:

public static void print(Set<String> s)

Então o método pode ser usado para todas as implementações de conjuntos.

Em teoria, devíamos fazer o mesmo. recomendação para listas interligadas, nomeadamente para salvar Referências LinkedList em variables of type List. No entanto, na biblioteca Java, a interface lista é comum tanto para a classe ArrayList quanto para a classe LinkedList. Em particular, ele tem métodos get e set para acesso aleatório, mesmo que esses métodos são muito ineficientes para listas vinculadas.

Tu não é possível escrever um código eficiente Se você não sabe se o acesso aleatório é eficiente ou não.

Isto é claramente um design sério. erro na biblioteca padrão, e não posso recomendar o uso a interface da lista por essa razão. Para ver o quão embaraçoso esse erro é, dê uma olhada o código-fonte do método binarySearch Colecções Classe. Esse método requer um Parâmetro da lista, mas a pesquisa binária não faz sentido para uma lista ligada. O código é então desajeitado tenta descobrir se a lista é uma lista vinculada, e então muda para uma pesquisa linear!

A interface Set e a Map interface, são bem concebidos, e você deve usá-los.

 24
Author: nazar_art, 2014-08-16 08:35:03

Eu uso (2) se o código for o "proprietário" da lista. Isto é, por exemplo, verdadeiro para as variáveis apenas locais. Não há razão para usar o tipo abstrato List em vez de ArrayList. Outro exemplo para demonstrar a propriedade:

public class Test {

    // This object is the owner of strings, so use the concrete type.
    private final ArrayList<String> strings = new ArrayList<>();

    // This object uses the argument but doesn't own it, so use abstract type.
    public void addStrings(List<String> add) {
        strings.addAll(add);
    }

    // Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
    public List<String> getStrings() {
        return Collections.unmodifiableList(strings);
    }

    // Here we create a new list and give ownership to the caller. Use concrete type.
    public ArrayList<String> getStringsCopy() {
        return new ArrayList<>(strings);
    }
}
 11
Author: mazatwork, 2013-04-21 12:20:37
{[[2]}acho que as pessoas que usam (2) não conhecem o princípio de substituição de Liskov ou o princípio de inversão de Dependência . Ou têm mesmo de usar ArrayList.
 9
Author: True Soft, 2010-02-17 07:55:27

Quando você escreve {[[0]}, você realmente diz, que o seu objecto implementa apenas a interface {[[0]}, mas não especifica a que classe pertence o seu objecto.

Quando escrever ArrayList, você indica que a sua classe de objectos é uma matriz dimensional.

Então, a primeira versão torna o seu código mais flexível no futuro.

Olha para os docs Java:

Classe ArrayList - implementação Redimensional da interface List.

Interface List - Uma colecção ordenada (também conhecida como sequência). O usuário desta interface tem controle preciso sobre onde na lista cada elemento é inserido.

Array - objeto recipiente que possui um número fixo de valores de um único tipo.

 9
Author: wzbozon, 2013-12-05 17:14:10
Na verdade, há ocasiões em que (2) não só é preferido, mas obrigatório e estou muito surpreso, que ninguém menciona isso aqui. Serialização!

Se você tem uma classe serializável e quer que ela contenha uma lista, então você deve declarar o campo como sendo de um tipo concreto e serializável como ArrayList porque a interface List não se extende java.io.Serializable

É óbvio que a maioria das pessoas não precisa de ser serializada e esquecer isto.

Exemplo:

public class ExampleData implements java.io.Serializable {

// The following also guarantees that strings is always an ArrayList.
private final ArrayList<String> strings = new ArrayList<>();
 9
Author: raudi, 2016-11-14 11:20:47

(3) Collection myCollection = new ArrayList ();

Estou a usar isto normalmente. E Se precisar de métodos de Lista, utilizarei a lista. O mesmo com o ArrayList. Você sempre pode mudar para uma interface mais "estreita", mas você não pode mudar para mais "ampla".
 8
Author: dart, 2010-02-17 07:51:38

Dos dois seguintes:

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();

O primeiro é geralmente preferido. Como irá utilizar apenas métodos da interface List, ela dá-lhe a liberdade de usar alguma outra implementação de List por exemplo LinkedList no futuro. Por isso, retira-te da implementação específica. Agora há dois pontos que vale a pena mencionar:

  1. devemos sempre programar para interface. Mais Aqui.
  2. quase sempre acabarás por usar ArrayList sobre LinkedList. Mais Aqui.

Estou a pensar se alguém usa (2)

Sim às vezes (ler raramente). Quando precisamos de métodos que façam parte da implementação de ArrayList mas não façam parte da interface List. Por exemplo ensureCapacity.

Também, com que frequência (e posso por favor obter um exemplo) a situação realmente necessita de usar (1) over (2)

Quase sempre você prefere a opção (1). Este é um padrão de design clássico em OOP onde você sempre tenta dissociar seu código de implementação específica e programa para a interface.

 6
Author: i_am_zero, 2017-05-23 11:33:24

O único caso que estou ciente de onde (2) pode ser Melhor é quando se usa GWT, porque reduz a pegada de aplicação (não é a minha ideia, mas a equipa do Google web toolkit diz isso). Mas para java regular executando dentro do JVM (1) é provavelmente sempre melhor.

 2
Author: Hans Westerbeek, 2010-02-17 07:54:29

A lista é um interface.It não tem métodos. Quando você chama o método em uma lista de referência. Ele de fato chama o método de "ArrayList" em ambos os casos.

E no futuro poderá mudar List obj = new ArrayList<> para List obj = new LinkList<> ou outro tipo que implemente A interface da lista.

 2
Author: Xar-e-ahmer Khan, 2015-10-27 11:50:44
[[4] alguém perguntou isso novamente (duplicado) o que me fez ir um pouco mais fundo sobre este assunto.
public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");

    ArrayList<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");

}

Se usarmos um visualizador bytecode (eu usei http://asm.ow2.org/eclipse/index.html ) vemos o seguinte (apenas a inicialização da lista e atribuição) para a nossa lista excerto:

   L0
    LINENUMBER 9 L0
    NEW ArrayList
    DUP
    INVOKESPECIAL ArrayList.<init> () : void
    ASTORE 1
   L1
    LINENUMBER 10 L1
    ALOAD 1: list
    LDC "a"
    INVOKEINTERFACE List.add (Object) : boolean
    POP
   L2
    LINENUMBER 11 L2
    ALOAD 1: list
    LDC "b"
    INVOKEINTERFACE List.add (Object) : boolean
    POP

E para :

   L3
    LINENUMBER 13 L3
    NEW java/util/ArrayList
    DUP
    INVOKESPECIAL java/util/ArrayList.<init> ()V
    ASTORE 2
   L4
    LINENUMBER 14 L4
    ALOAD 2
    LDC "a"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L5
    LINENUMBER 15 L5
    ALOAD 2
    LDC "b"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP

A diferença é lista acaba chamando INVOKEINTERFACE considerando aList chama INVOKEVIRTUAL. A aceder à referência do 'Plugin' de contorno do Código Bycode,

Invokeinterface é usado para invocar um método declarado dentro de um Java interface

While invokevirtual

Invoca todos os métodos excepto os métodos de interface (que utilizam invokeinterface), métodos estáticos (que usam invokestatic) , e os poucos casos especiais tratados pela invokespecial.

Em resumo, o invokevirtual aparece objectivo fora da pilha enquanto para o invokeinterface

O interpretador tira ' n 'itens da pilha de operandos, onde' n ' é um 8-bits sem sinal parâmetro inteiro retirado do bytecode. O primeiro destes itens é objectref, uma referência ao objeto cujo método está sendo chamado.

Se eu entendo isto correctamente,a diferença é basicamente como cada maneira recupera objectref .

 2
Author: toxicafunk, 2016-12-23 11:06:00
Eu diria que é preferível 1, a menos que
  • Você está dependendo da implementação do comportamento opcional* na lista, nesse caso usando explicitamente a lista é mais claro
  • você estará usando o ArrayList em uma chamada de método que requer ArrayList, possivelmente para comportamento opcional ou características de desempenho

Meu palpite é que em 99% dos casos você pode obter com a lista, que é preferível.

  • por exemplo removeAll, ou add(null)
 1
Author: extraneon, 2010-02-17 07:47:58

List a interface tem várias classes diferentes - ArrayList e LinkedList. LinkedList é usado para criar uma colecção indexada e ArrayList - para criar listas ordenadas. Então, você pode usar qualquer um deles em seus argumentos, mas você pode permitir que outros desenvolvedores que usam seu código, biblioteca, etc. para usar diferentes tipos de listas, não apenas as que você usa, então, neste método

ArrayList<Object> myMethod (ArrayList<Object> input) {
   // body
}

Só o pode usar com ArrayList, não com LinkedList, mas pode permitir usar qualquer uma das classes List noutros locais onde o método é usando, é apenas a sua escolha, então usando uma interface pode permitir:

List<Object> myMethod (List<Object> input) {
   // body
}

Neste método, pode usar qualquer uma das classes List que queira usar:

List<Object> list = new ArrayList<Object> ();

list.add ("string");

myMethod (list);

Conclusão:

Use as interfaces em todos os lugares quando possível, não restrinja você ou outros a usar métodos diferentes que eles querem usar.

 0
Author: Acuna, 2018-06-15 21:43:29