Fiquei surpreso com o fato de que Map<?,?>
não é um Collection<?>
.
Eu pensei que faria muito sentido se fosse declarado como tal:
public interface Map<K,V> extends Collection<Map.Entry<K,V>>
Afinal, a Map<K,V>
é uma coleção de Map.Entry<K,V>
, não é?
Então, há uma boa razão para que não seja implementado como tal?
Agradeço a Cletus pela resposta mais autoritária, mas ainda estou me perguntando por que, se você já pode ver um Map<K,V>
como Set<Map.Entries<K,V>>
(via entrySet()
), ele não apenas estende essa interface.
Se a
Map
é aCollection
, quais são os elementos? A única resposta razoável é "pares de valores-chave"
Exatamente, interface Map<K,V> extends Set<Map.Entry<K,V>>
seria ótimo!
mas isso fornece uma
Map
abstração muito limitada (e não particularmente útil) .
Mas se for esse o caso, por que é entrySet
especificado pela interface? Deve ser útil de alguma forma (e acho que é fácil argumentar sobre essa posição!).
Você não pode perguntar para qual valor uma determinada chave é mapeada nem excluir a entrada de uma determinada chave sem saber para qual valor ela é mapeada.
Não estou dizendo que é só isso Map
! Pode e deve manter todos os outros métodos (exceto entrySet
agora redundantes)!
fonte
Respostas:
Nas Perguntas frequentes sobre o design da API Java Collections :
Atualização: acho que a citação responde à maioria das perguntas. Vale ressaltar que a parte de uma coleção de entradas não é uma abstração particularmente útil. Por exemplo:
permitiria:
(assumindo um
entry()
método que cria umaMap.Entry
instância)Map
s exigem chaves exclusivas, portanto isso violaria isso. Ou se você impõe chaves exclusivas em umaSet
das entradas, não é realmente umaSet
no sentido geral. É umSet
com mais restrições.Indiscutivelmente, você poderia dizer que o relacionamento
equals()
/ estava puramente essencial, mas mesmo isso tem problemas. Mais importante, isso realmente agrega algum valor? Você pode achar que essa abstração é interrompida quando você começa a examinar os casos de canto.hashCode()
Map.Entry
Vale a pena notar que o
HashSet
é realmente implementado como umHashMap
, e não o contrário. Isso é puramente um detalhe de implementação, mas é interessante, no entanto.A principal razão de
entrySet()
existir é simplificar a travessia para que você não precise atravessar as teclas e, em seguida, faça uma busca na chave. Não tome isso como evidência prima facie de que aMap
deve ser umaSet
das entradas (imho).fonte
entrySet()
suportaremove
, enquanto não suportaadd
(provavelmente lançaUnsupportedException
). Então eu vejo o seu ponto, mas, novamente, não vejo o ponto do OP, também .. (Minha opinião é como indicado na minha própria resposta ..)add
podem ser tratadas da mesma maneira que aentrySet()
exibição: permite algumas operações e outras não. Uma resposta natural, é claro, é "Que tipo deSet
suporte não é suportadoadd
?" - bem, se esse comportamento é aceitávelentrySet()
, por que não é aceitávelthis
? Dito isto, já estou bastante convencido de por que essa não é uma idéia tão boa quanto eu pensava, mas ainda acho que vale a pena um debate adicional, apenas para enriquecer minha própria compreensão do que faz um bom design de API / OOP.Collection
estenderMap
?Embora você tenha recebido várias respostas que abordam sua pergunta de maneira bastante direta, acho que pode ser útil recuar um pouco e analisar a questão de maneira mais geral. Ou seja, para não olhar especificamente como a biblioteca Java é escrita, e por que ela é escrita dessa maneira.
O problema aqui é que a herança apenas modela um tipo de semelhança. Se você escolher duas coisas que pareçam "parecidas com coleções", provavelmente poderá escolher 8 ou 10 coisas que elas têm em comum. Se você escolher um par diferente de itens "semelhantes a coleções", eles também terão 8 ou 10 itens em comum - mas não serão os mesmos 8 ou 10 itens do primeiro par.
Se você observar uma dúzia de coisas "semelhantes a coleções", virtualmente todas elas provavelmente terão algo em torno de 8 ou 10 características em comum com pelo menos uma outra - mas se você observar o que é compartilhado em todos os um deles, você fica com praticamente nada.
Essa é uma situação em que a herança (especialmente a herança única) simplesmente não é um modelo adequado. Não existe uma linha divisória limpa entre quais são realmente coleções e quais não são - mas se você deseja definir uma classe Collection significativa, você fica impedido de deixar algumas delas de fora. Se você deixar apenas alguns deles de fora, sua classe Collection poderá fornecer apenas uma interface bastante esparsa. Se você deixar mais de fora, poderá oferecer uma interface mais rica.
Alguns também adotam a opção de dizer basicamente: "esse tipo de coleção suporta a operação X, mas você não pode usá-la, derivando de uma classe base que define X, mas tentando usar a classe derivada 'X falha (por exemplo, , lançando uma exceção).
Isso ainda deixa um problema: quase independentemente de qual você deixa de fora e de quem coloca, você terá que traçar uma linha dura entre quais classes estão e quais estão fora. Não importa onde você traça essa linha, você ficará com uma divisão clara, bastante artificial, entre algumas coisas que são bastante semelhantes.
fonte
Eu acho que o porquê é subjetivo.
Em C #, acho que
Dictionary
estende ou pelo menos implementa uma coleção:No Pharo Smalltak também:
Mas há uma assimetria com alguns métodos. Por exemplo,
collect:
aceita associação (o equivalente a uma entrada), enquantodo:
aceita os valores. Eles fornecem outro métodokeysAndValuesDo:
para iterar o dicionário por entrada.Add:
leva uma associação, masremove:
foi "suprimido":Portanto, é definitivamente factível, mas leva a outros problemas relacionados à hierarquia de classes.
O que é melhor é subjetivo.
fonte
A resposta do cletus é boa, mas quero adicionar uma abordagem semântica. Para combinar os dois, não faz sentido, pense no caso de adicionar um par de valores-chave por meio da interface de coleção e a chave já existe. A interface do mapa permite apenas um valor associado à chave. Mas se você remover automaticamente a entrada existente com a mesma chave, a coleção terá o mesmo tamanho de antes de adicionar - muito inesperado para uma coleção.
fonte
Set
funciona, eSet
não implementarCollection
.Coleções Java estão quebradas. Há uma interface ausente, a de Relation. Portanto, o mapa estende a relação estende o conjunto. As relações (também chamadas de multi-mapas) têm pares nome-valor exclusivos. Os mapas (também conhecidos como "Funções") têm nomes (ou chaves) exclusivos que, naturalmente, são mapeados para valores. Sequências estendem Mapas (onde cada tecla é um número inteiro> 0). Bolsas (ou conjuntos múltiplos) estendem Mapas (onde cada tecla é um elemento e cada valor é o número de vezes que o elemento aparece na bolsa).
Essa estrutura permitiria a interseção, união etc. de uma variedade de "coleções". Portanto, a hierarquia deve ser:
Sun / Oracle / Java ppl - por favor, acerte da próxima vez. Obrigado.
fonte
Se você olhar para a respectiva estrutura de dados, poderá adivinhar facilmente por que
Map
não faz parteCollection
. CadaCollection
um armazena um único valor em que, como umMap
par de valores-chave, armazena. Portanto, os métodos naCollection
interface são incompatíveis para aMap
interface. Por exemplo,Collection
nós temosadd(Object o)
. Em que seria essa implementaçãoMap
? Não faz sentido ter esse métodoMap
. Em vez disso, temos umput(key,value)
método emMap
.Mesmo argumento vale para
addAll()
,remove()
eremoveAll()
métodos. Portanto, o principal motivo é a diferença na maneira como os dados são armazenadosMap
eCollection
. Além disso, se você se lembrar daCollection
interface implementada pelaIterable
interface, ou seja, qualquer interface com o.iterator()
método deve retornar um iterador que deve permitir a iteração sobre os valores armazenados noCollection
. Agora, o que esse método retornaria para umMap
? Iterador de chaves ou um iterador de valor? Isso também não faz sentido.Existem maneiras pelas quais podemos iterar sobre armazenamentos de chaves e valores em um
Map
e é assim que faz parte daCollection
estrutura.fonte
There are ways in which we can iterate over keys and values stores in a Map and that is how it is a part of Collection framework.
Você pode mostrar um exemplo de código para isso?add(Object o)
seria adicionar umEntry<ClassA, ClassB>
objeto. AMap
pode ser considerado umCollection of Tuples
Na verdade, se fosse
implements Map<K,V>, Set<Map.Entry<K,V>>
, então eu tendem a concordar .. Parece até natural. Mas isso não funciona muito bem, certo? Digamos que temosHashMap implements Map<K,V>, Set<Map.Entry<K,V>
,LinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V>
etc ... tudo bem, mas se você tivesseentrySet()
, ninguém esquecerá de implementar esse método, e você pode ter certeza de que pode obter entrySet para qualquer mapa, enquanto não está se estiver esperando que o implementador implementou as duas interfaces ...A razão que eu não quero ter
interface Map<K,V> extends Set<Map.Entry<K,V>>
é simplesmente, porque haverá mais métodos. E afinal, são coisas diferentes, certo? Também, na prática, se eu clicarmap.
no IDE, não quero ver.remove(Object obj)
, e.remove(Map.Entry<K,V> entry)
porque não posso fazerhit ctrl+space, r, return
e acabar com ele.fonte
Map<K,V>
não deve se estender,Set<Map.Entry<K,V>>
pois:Map.Entry
s com a mesma chave à mesmaMap
, masMap.Entry
s com a mesma chave à mesmaSet<Map.Entry>
.fonte
Direto e simples. Collection é uma interface que espera apenas um Objeto, enquanto o Mapa requer Dois.
fonte