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.)
15 answers
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.
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(...)
ouArrays.asList(...)
não devolvam umArrayList
.Os Métodos na API
List
não garantem devolver uma lista do mesmo tipo.
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.
A parte "quantas vezes" da pergunta é objectivamente irrespondível.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.)
(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.)
É um ponto válido. No entanto, Java oferece melhores maneiras de lidar com isso; por exemploVocê não pode escrever um código eficiente se você não sabe se o acesso aleatório é eficiente ou não.
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.)
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:
(publicado principalmente para o diagrama de colecções)
É 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 classeArrayList
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étodobinarySearch
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.
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);
}
}
ArrayList
.
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.
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.
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
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<>();
(3) Collection myCollection = new ArrayList ();
Estou a usar isto normalmente. E só 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".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:
- devemos sempre programar para interface. Mais Aqui.
- quase sempre acabarás por usar
ArrayList
sobreLinkedList
. 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.
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.
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.
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 .
- 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
, ouadd(null)
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.