Se eu tiver o valor "foo"e um HashMap<String> ftwpara o qual ftw.containsValue("foo")retorne true, como posso obter a chave correspondente? Preciso percorrer o hashmap? Qual o melhor jeito pra fazer isso?
Observe que não há uma chave correspondente única - é possível que haja várias chaves mapeadas para o mesmo valor.
CPerkins
1
@CPerkins, mas para hashmaps como <strFilename, Reader> e muitos outros 1 a 1, é muito útil.
Poder de Aquário
se você tiver uma pequena coleção de itens, considere criar constantes public static final String TIME = "time";eproperties.put(TIME, PbActivityJpa_.time);
Se você optar por usar a biblioteca Commons Collections em vez da API Java Collections padrão, poderá conseguir isso com facilidade.
O BidiMap interface na biblioteca Coleções é um mapa bidirecional, permitindo mapear uma chave para um valor (como mapas normais) e também mapear um valor para uma chave, permitindo realizar pesquisas nas duas direções. A obtenção de uma chave para um valor é suportada pelo método getKey () .
No entanto, existe uma ressalva: os mapas de bidi não podem ter vários valores mapeados para chaves e, portanto, a menos que seu conjunto de dados tenha mapeamentos 1: 1 entre chaves e valores, você não pode usar bidimaps.
Atualizar
Se você deseja confiar na API Java Collections, precisará garantir o relacionamento 1: 1 entre chaves e valores no momento da inserção do valor no mapa. Isto é mais fácil dizer do que fazer.
Depois de garantir isso, use o método entrySet () para obter o conjunto de entradas (mapeamentos) no Mapa. Depois de obter o conjunto cujo tipo é Map.Entry , percorra as entradas, comparando o valor armazenado com o esperado e obtenha a chave correspondente .
Atualização # 2
O suporte para mapas bidi com genéricos pode ser encontrado no Google Guava e nas bibliotecas Commons-Collections refatoradas (o último não é um projeto Apache). Agradecemos a Esko por apontar o suporte genérico ausente no Apache Commons Collections. O uso de coleções com genéricos torna o código mais sustentável.
... e se você gosta de genéricos e todo esse material moderno, o Google Collections possui o BiMap, onde você pode obter o valor especificado correspondente à chave, chamando biMap.inverse (). get (value);
Esko
1
Sim, o Apache Commons Collections não suporta genéricos. No entanto, existe o Google Collections, como você apontou (que ainda não uso - ainda não há versão 1.0), e há o Commons-Collections refatorado com suporte para genéricos. Você vai encontrar isso como um projeto Sourceforge @ sourceforge.net/projects/collections
Vineet Reynolds
2
As coleções do Google não são uma versão refatorada do Commons-Collections.
Whiskeyierra
12
@whiskeysierra: Acho que ninguém está dizendo atualmente.
Se sua estrutura de dados possui mapeamento muitos-para-um entre chaves e valores, você deve iterar sobre as entradas e escolher todas as chaves adequadas:
Você pode dizer algo sobre o desempenho? O que será mais otimizado? Este ou BidiMap?
tasomaniac
Eu pensei na mesma solução, eu a votei naturalmente, mas duvido de sua eficiência quando se trata de coleções realmente grandes.
arjacsoh
3
stackoverflow.com/questions/4553624/hashmap-get-put-complexity O HashMap possui complexidade de tempo o(1). Se você estiver iterando sobre os valores, isso prejudicará o desempenho. Se você quiser um better performancee tem um one-onerelacionamento, você pode usar another mapondevalue is a key
veer7
3
Eu recomendo para substituir .filter(entry -> entry.getValue().equals(value))com nenhuma declaração sobre a capacidade foi feita. Além disso, você pode substituir por.filter(entry ->Objects.equals(entry.getValue(), value))null.map(entry -> entry.getKey()).map(Map.Entry::getKey)
Holger
Estou tendo dificuldade para entender a notação <T, E> antes de Set <T> getKeysByValue () ... qual é o sentido .... maneira diferente de fazer isso sem usar isso? agradecimentos
Algumas informações adicionais ... Pode ser útil para você
O método acima pode não ser bom se o seu hashmap for realmente grande. Se o seu hashmap contiver uma chave exclusiva para o mapeamento de valor exclusivo, você poderá manter mais um hashmap que contenha o mapeamento de Valor para Chave.
Ou seja, você tem que manter dois hashmaps
1.Key to value
2.Value to key
Nesse caso, você pode usar o segundo hashmap para obter a chave.
Use uma implementação de mapa criada para isso, como o BiMap das coleções do Google. Observe que o BiMap das coleções do Google requer valores exclusivos, além de chaves, mas fornece alto desempenho no desempenho de ambas as direções
Manter manualmente dois mapas - um para chave -> valor e outro mapa para valor -> chave
Faça uma iteração entre entrySet()e para encontrar as chaves que correspondem ao valor. Esse é o método mais lento, pois requer iteração em toda a coleção, enquanto os outros dois métodos não exigem isso.
Isso não está realmente correto. Isso não requer apenas 1 a 1, mas também que o conjunto de valores é separado do conjunto de chaves. Você não pode aplicar isso ao mapa bijetivo {1 -> 2, 2 -> 3}: 2 é um valor e uma chave.
Luis A. Florit 04/02
15
Decore o mapa com sua própria implementação
classMyMap<K,V>extendsHashMap<K, V>{Map<V,K> reverseMap =newHashMap<V,K>();@Overridepublic V put(K key, V value){// TODO Auto-generated method stub
reverseMap.put(value, key);returnsuper.put(key, value);}public K getKey(V value){return reverseMap.get(value);}}
Eu acho que essa é uma abordagem interessante, mas, como a relação tem que ser 1: 1, eu me livraria completamente do HashMap e implementaria a interface Map <K, V> para evitar duplicatas de valores e chaves.
Fran Marzoa
11
Não há resposta inequívoca, porque várias chaves podem ser mapeadas para o mesmo valor. Se você estiver aplicando exclusividade com seu próprio código, a melhor solução é criar uma classe que use dois Hashmaps para rastrear os mapeamentos nas duas direções.
Inteligente, mas e se houver 2 ou mais objetos KeyValue que contenham o mesmo valor? Qual chave você deve escolher?
Vineet Reynolds
2
@ Vinine, não vejo como essa abordagem resolve a questão do OP. o que você quis dizer com "Então, quando você tem um valor, você também tem a chave".
Qiang Li
9
Eu acho que esta é a melhor solução, endereço original: Java2s
import java.util.HashMap;import java.util.Map;publicclassMain{publicstaticvoid main(String[] argv){Map<String,String> map =newHashMap<String,String>();
map.put("1","one");
map.put("2","two");
map.put("3","three");
map.put("4","four");System.out.println(getKeyFromValue(map,"three"));}// hm is the map you are trying to get value from itpublicstaticObject getKeyFromValue(Map hm,Object value){for(Object o : hm.keySet()){if(hm.get(o).equals(value)){return o;}}returnnull;}}
Um uso fácil: se você colocar todos os dados no hasMap e tiver o item = "Automobile", estará procurando a chave no hashMap. essa é uma boa solução.
Sim, é exatamente isso que faz. Mas é claro que ele retorna verdadeiro assim que encontra um valor para o qual .equals é verdadeiro, em oposição ao que o OP provavelmente precisará fazer.
cperkins
1
Bem, a iteração nas entradas pode retornar com a chave assim que encontrar um valor correspondente também. Várias correspondências não pareciam ser uma preocupação.
Jonas K
6
Para a API de segmentação de desenvolvimento para Android <19, a solução de relacionamento individual Vitalii Fedorenko não funciona porque Objects.equalsnão foi implementada. Aqui está uma alternativa simples:
public<K, V> K getKeyByValue(Map<K, V> map, V value){for(Map.Entry<K, V> entry : map.entrySet()){if(value.equals(entry.getValue())){return entry.getKey();}}returnnull;}
Esta solução funciona para mim; também desenvolvendo para uma versão arqueológica do Android, no meu caso, para obter a chave de um marcador do Google Map em um mapa em um evento "onMarkerClick". Iterar o entrySet funciona; mas iterar as chaves e combiná-las às entradas com get () e comparar a saída, não.
É bom que você queira fornecer coisas úteis, mas não deve ser uma resposta "apenas código" e o código em si também não deve estar cheio de odores de código.
Tom
2
Sim, você precisa percorrer o hashmap, a menos que implemente algo ao longo do que essas várias respostas sugerem. Em vez de mexer com o entrySet, eu apenas pegaria o keySet (), iteraria sobre esse conjunto e manteria a (primeira) chave que fornece seu valor correspondente. Se você precisar de todas as chaves que correspondam a esse valor, obviamente precisará fazer a coisa toda.
Como Jonas sugere, isso já pode ser o que o método containsValue está fazendo, portanto, você pode pular esse teste todos juntos e fazer a iteração sempre (ou talvez o compilador já elimine a redundância, quem sabe).
Além disso, em relação às outras respostas, se o seu mapa reverso parecer
Map<Value,Set<Key>>
você pode lidar com mapeamentos de chave-> valor não exclusivos, se precisar dessa capacidade (desmembrando-os). Isso seria incorporado em qualquer uma das soluções sugeridas pelas pessoas aqui usando dois mapas.
Eu acho que essa resposta poderia ser melhorada adicionando uma explicação.
Jonathan
2
-1. Eu testei com Stringcomo chave e valor. Quando ligo map.add("1", "2"); map.add("1","3");, posso ligar map.getKey("2");e recuperar "1", mesmo que "1"seja a chave "3".
jlordo
@ Jonathan, a idéia por trás dessa classe é manter outro HashMap com os mapeamentos reversos para que, além de recuperar um valor de uma chave, você possa recuperar uma chave de um valor. As classes T1 e T2 são um pouco confusas; talvez literalmente os chame de Chave e Valor? Embora eu esperasse a capacidade de receber mais de um valor ou mais de uma chave em troca, dependendo dos dados e do que você deseja. Use com cautela
Chicowitz
1
@theknightwhosaysni "1" não é a chave para "2" (mais). Essa também é a resposta para sua pergunta, a chamada getValue("1")retornará 3.
jlordo
Desculpe jlordo, eu estava enganado sobre o comportamento padrão do Hashmap: você está certo ao adicionar um novo valor para uma chave substituir o valor antigo #
O que acontece se várias chaves tiverem o mesmo valor?
Cà phê em
Quando você passar as várias chaves com o mesmo valor, obteremos a última chave como resultado. exemplo: Uma saída 1, B 1, C 1, D 2: se passarmos o valor 1, a saída será C
Amazing India
@AmazingIndia Isso não é garantido e depende completamente da implementação específica do mapa. O HashMap, por exemplo, não garante um pedido; portanto, você não tem idéia de qual saída será retornada aqui.
Niels Doucet
1
import java.util.HashMap;import java.util.HashSet;import java.util.Set;publicclassValueKeysMap<K, V>extendsHashMap<K,V>{HashMap<V,Set<K>>ValueKeysMap=newHashMap<V,Set<K>>();@Overridepublicboolean containsValue(Object value){returnValueKeysMap.containsKey(value);}@Overridepublic V put(K key, V value){if(containsValue(value)){Set<K> keys =ValueKeysMap.get(value);
keys.add(key);}else{Set<K> keys =newHashSet<K>();
keys.add(key);ValueKeysMap.put(value, keys);}returnsuper.put(key, value);}@Overridepublic V remove(Object key){
V value =super.remove(key);Set<K> keys =ValueKeysMap.get(value);
keys.remove(key);if(keys.size()==0){ValueKeysMap.remove(value);}return value;}publicSet<K> getKeys4ThisValue(V value){Set<K> keys =ValueKeysMap.get(value);return keys;}publicboolean valueContainsThisKey(K key, V value){if(containsValue(value)){Set<K> keys =ValueKeysMap.get(value);return keys.contains(key);}returnfalse;}/*
* Take care of argument constructor and other api's like putAll
*/}
Meus 2 centavos. Você pode obter as chaves em uma matriz e depois percorrer a matriz. Isso afetará o desempenho desse bloco de código se o mapa for muito grande, onde você está obtendo as chaves em uma matriz primeiro, o que pode consumir algum tempo e, em seguida, você faz um loop. Caso contrário, para mapas menores, tudo ficará bem.
String[] keys = yourMap.keySet().toArray(newString[0]);for(int i =0; i < keys.length ; i++){//This is your key String key = keys[i];//This is your value
yourMap.get(key)}
E por que alguém deveria usar essa abordagem? Como você já disse, o desempenho seria pior do que em outras abordagens.
Tom
1
Eu acho que keySet () pode ser bom para encontrar as chaves mapeadas para o valor e ter um estilo de codificação melhor do que entrySet () .
Ex:
Suponha que você tenha um mapa HashMap , ArrayList res , um valor para o qual deseja encontrar todo o mapeamento de chaves e, em seguida, armazene as chaves nos res .
map.get(key) == valuenão é uma boa ideia ao verificar a igualdade de objetos, pois você está comparando referências. Igualdade do objeto deve sempre usar o seu.equals()
frododot
1
Embora isso não responda diretamente à pergunta, está relacionado.
Dessa forma, você não precisa continuar criando / iterando. Basta criar um mapa reverso uma vez e obter o que você precisa.
/**
* Both key and value types must define equals() and hashCode() for this to work.
* This takes into account that all keys are unique but all values may not be.
*
* @param map
* @param <K>
* @param <V>
* @return
*/publicstatic<K, V>Map<V,List<K>> reverseMap(Map<K,V> map){if(map ==null)returnnull;Map<V,List<K>> reverseMap =newArrayMap<>();for(Map.Entry<K,V> entry : map.entrySet()){
appendValueToMapList(reverseMap, entry.getValue(), entry.getKey());}return reverseMap;}/**
* Takes into account that the list may already have values.
*
* @param map
* @param key
* @param value
* @param <K>
* @param <V>
* @return
*/publicstatic<K, V>Map<K,List<V>> appendValueToMapList(Map<K,List<V>> map, K key, V value){if(map ==null|| key ==null|| value ==null)return map;List<V> list = map.get(key);if(list ==null){List<V> newList =newArrayList<>();
newList.add(value);
map.put(key, newList);}else{
list.add(value);}return map;}
É importante observar que, desde esta pergunta, o Apache Collections suporta BidiMaps genéricos . Portanto, algumas das principais respostas votadas não são mais precisas nesse ponto.
Para um BidiMap serializado que também suporta valores duplicados (cenário de 1 para muitos), considere o MapDB.org .
public static final String TIME = "time";
eproperties.put(TIME, PbActivityJpa_.time);
Respostas:
Se você optar por usar a biblioteca Commons Collections em vez da API Java Collections padrão, poderá conseguir isso com facilidade.
O BidiMap interface na biblioteca Coleções é um mapa bidirecional, permitindo mapear uma chave para um valor (como mapas normais) e também mapear um valor para uma chave, permitindo realizar pesquisas nas duas direções. A obtenção de uma chave para um valor é suportada pelo método getKey () .
No entanto, existe uma ressalva: os mapas de bidi não podem ter vários valores mapeados para chaves e, portanto, a menos que seu conjunto de dados tenha mapeamentos 1: 1 entre chaves e valores, você não pode usar bidimaps.
Atualizar
Se você deseja confiar na API Java Collections, precisará garantir o relacionamento 1: 1 entre chaves e valores no momento da inserção do valor no mapa. Isto é mais fácil dizer do que fazer.
Depois de garantir isso, use o método entrySet () para obter o conjunto de entradas (mapeamentos) no Mapa. Depois de obter o conjunto cujo tipo é Map.Entry , percorra as entradas, comparando o valor armazenado com o esperado e obtenha a chave correspondente .
Atualização # 2
O suporte para mapas bidi com genéricos pode ser encontrado no Google Guava e nas bibliotecas Commons-Collections refatoradas (o último não é um projeto Apache). Agradecemos a Esko por apontar o suporte genérico ausente no Apache Commons Collections. O uso de coleções com genéricos torna o código mais sustentável.
fonte
Se sua estrutura de dados possui mapeamento muitos-para-um entre chaves e valores, você deve iterar sobre as entradas e escolher todas as chaves adequadas:
No caso de um relacionamento individual, você pode retornar a primeira chave correspondente:
No Java 8:
Além disso, para usuários do Guava, o BiMap pode ser útil. Por exemplo:
fonte
o(1)
. Se você estiver iterando sobre os valores, isso prejudicará o desempenho. Se você quiser umbetter performance
e tem umone-one
relacionamento, você pode usaranother map
ondevalue is a key
.filter(entry -> entry.getValue().equals(value))
com nenhuma declaração sobre a capacidade foi feita. Além disso, você pode substituir por.filter(entry ->
Objects.equals
(entry.getValue(), value))
null
.map(entry -> entry.getKey())
.map(Map.Entry::getKey)
Algumas informações adicionais ... Pode ser útil para você
O método acima pode não ser bom se o seu hashmap for realmente grande. Se o seu hashmap contiver uma chave exclusiva para o mapeamento de valor exclusivo, você poderá manter mais um hashmap que contenha o mapeamento de Valor para Chave.
Ou seja, você tem que manter dois hashmaps
Nesse caso, você pode usar o segundo hashmap para obter a chave.
fonte
Eu acho que suas escolhas são
entrySet()
e para encontrar as chaves que correspondem ao valor. Esse é o método mais lento, pois requer iteração em toda a coleção, enquanto os outros dois métodos não exigem isso.fonte
Você pode inserir o par de chave, valor e seu inverso na estrutura do mapa
O uso de map.get ("theValue") retornará "theKey".
É uma maneira rápida e suja de criar mapas constantes, que só funcionarão para alguns poucos conjuntos de dados selecionados:
fonte
Decore o mapa com sua própria implementação
fonte
Não há resposta inequívoca, porque várias chaves podem ser mapeadas para o mesmo valor. Se você estiver aplicando exclusividade com seu próprio código, a melhor solução é criar uma classe que use dois Hashmaps para rastrear os mapeamentos nas duas direções.
fonte
Para encontrar todas as chaves que são mapeadas para esse valor, itere através de todos os pares no hashmap, usando
map.entrySet()
.fonte
Usando o Java 8:
fonte
value=="foo"
isso não vai funcionar.equals
deve ser usado para comparar seqüências de caracteres.value
tenha sido internado.Se você criar o mapa em seu próprio código, tente colocar a chave e o valor no mapa juntos:
Então, quando você tem um valor, você também tem a chave.
fonte
Eu acho que esta é a melhor solução, endereço original: Java2s
Um uso fácil: se você colocar todos os dados no hasMap e tiver o item = "Automobile", estará procurando a chave no hashMap. essa é uma boa solução.
fonte
Receio que você apenas precise iterar seu mapa. O mais curto que eu consegui:
fonte
fonte
Parece que a melhor maneira é iterar as entradas usando,
map.entrySet()
já quemap.containsValue()
provavelmente faz isso de qualquer maneira.fonte
Para a API de segmentação de desenvolvimento para Android <19, a solução de relacionamento individual Vitalii Fedorenko não funciona porque
Objects.equals
não foi implementada. Aqui está uma alternativa simples:fonte
Você pode usar o abaixo:
fonte
Sim, você precisa percorrer o hashmap, a menos que implemente algo ao longo do que essas várias respostas sugerem. Em vez de mexer com o entrySet, eu apenas pegaria o keySet (), iteraria sobre esse conjunto e manteria a (primeira) chave que fornece seu valor correspondente. Se você precisar de todas as chaves que correspondam a esse valor, obviamente precisará fazer a coisa toda.
Como Jonas sugere, isso já pode ser o que o método containsValue está fazendo, portanto, você pode pular esse teste todos juntos e fazer a iteração sempre (ou talvez o compilador já elimine a redundância, quem sabe).
Além disso, em relação às outras respostas, se o seu mapa reverso parecer
você pode lidar com mapeamentos de chave-> valor não exclusivos, se precisar dessa capacidade (desmembrando-os). Isso seria incorporado em qualquer uma das soluções sugeridas pelas pessoas aqui usando dois mapas.
fonte
Você pode obter a chave usando valores usando o seguinte código.
fonte
fonte
String
como chave e valor. Quando ligomap.add("1", "2"); map.add("1","3");
, posso ligarmap.getKey("2");
e recuperar"1"
, mesmo que"1"
seja a chave"3"
.getValue("1")
retornará3
.Em java8
fonte
fonte
fonte
fonte
fonte
Use um invólucro fino: HMap
fonte
Meus 2 centavos. Você pode obter as chaves em uma matriz e depois percorrer a matriz. Isso afetará o desempenho desse bloco de código se o mapa for muito grande, onde você está obtendo as chaves em uma matriz primeiro, o que pode consumir algum tempo e, em seguida, você faz um loop. Caso contrário, para mapas menores, tudo ficará bem.
fonte
Eu acho que keySet () pode ser bom para encontrar as chaves mapeadas para o valor e ter um estilo de codificação melhor do que entrySet () .
Ex:
Suponha que você tenha um mapa HashMap , ArrayList res , um valor para o qual deseja encontrar todo o mapeamento de chaves e, em seguida, armazene as chaves nos res .
Você pode escrever o código abaixo:
em vez de usar entrySet () abaixo:
Espero que ajude :)
fonte
map.get(key) == value
não é uma boa ideia ao verificar a igualdade de objetos, pois você está comparando referências. Igualdade do objeto deve sempre usar o seu.equals()
Embora isso não responda diretamente à pergunta, está relacionado.
Dessa forma, você não precisa continuar criando / iterando. Basta criar um mapa reverso uma vez e obter o que você precisa.
fonte
É importante observar que, desde esta pergunta, o Apache Collections suporta BidiMaps genéricos . Portanto, algumas das principais respostas votadas não são mais precisas nesse ponto.
Para um BidiMap serializado que também suporta valores duplicados (cenário de 1 para muitos), considere o MapDB.org .
fonte
Se você deseja obter a chave do valor, é melhor usar o bidimapa (mapas bidirecionais), é possível obter a chave do valor no tempo O (1).
Mas a desvantagem disso é que você só pode usar um conjunto de chaves e um conjunto de valores exclusivos.
Existe uma estrutura de dados chamada Tabela em java, que nada mais é do que mapa de mapas como
Tabela <A, B, C> == mapa <A, mapa <B, C>>
Aqui você pode
map<B,C>
consultarT.row(a);
, e tambémmap<A,C>
consultarT.column(b);
No seu caso especial, insira C como uma constante.
Então, é como <a1, b1, 1> <a2, b2, 1>, ...
Portanto, se você encontrar via T.row (a1) ---> retorna o mapa de -> obtenha o conjunto de chaves deste mapa retornado.
Se você precisar encontrar o valor da chave, T.column (b2) -> retorna o mapa de -> obtém o conjunto de chaves do mapa retornado.
Vantagens em relação ao caso anterior:
fonte