Java Hashmap:如何从值得到键?

如果我有值"foo"和一个HashMap<String> ftw,其中ftw.containsValue("foo")返回true,我如何获得相应的键?我需要遍历hashmap吗?最好的方法是什么?

1124529 次浏览

没有明确的答案,因为多个键可以映射到相同的值。如果要在自己的代码中强制执行唯一性,最好的解决方案是创建一个类,该类使用两个hashmap在两个方向上跟踪映射。

要找到映射到该值的所有键,请使用map.entrySet()遍历hashmap中的所有对。

听起来最好的方法是使用map.entrySet()遍历条目,因为map.containsValue()可能会这样做。

如果你选择使用公共馆藏图书馆而不是标准的Java集合框架,你可以很容易地实现这一点。

Collections库中的BidiMap接口是一个双向映射,允许你将一个键映射到一个值(像法线映射一样),也允许你将一个值映射到一个键,因此允许你在两个方向上执行查找。getKey()方法支持为值获取键。

但有一个警告,bidi映射不能有多个值映射到键,因此,除非您的数据集在键和值之间有1:1的映射,否则不能使用bidi映射。


如果您希望依赖Java Collections API,那么在将值插入映射时,必须确保键和值之间的1:1关系。说起来容易做起来难。

一旦你可以确保,使用entrySet()方法来获取Map中的条目(映射)集。一旦你获得了类型为Map.Entry的集合,遍历条目,将储值与期望的比较,并获得相应的关键


对带有泛型的bidi映射的支持可以在谷歌番石榴和重构的Commons-Collections库中找到(后者不是Apache项目)。感谢Esko指出Apache Commons Collections中缺少的泛型支持。使用带有泛型的集合使代码更具可维护性。


版本4.0开始,官方Apache Commons Collections™库支持泛型< em > <强> < /强> < / em >

参见“;org.apache.commons.collections4.bidimap"现在支持Java 泛型BidiMapOrderedBidiMapSortedBidiMap接口的可用实现列表。

我认为你的选择是

  • 使用为此构建的映射实现,如谷歌集合中的BiMap。注意,谷歌集合BiMap要求值和键的唯一性,但它在两个方向上都提供了高性能
  • 手动维护两个映射——一个用于key -> value,另一个用于value -> key
  • 遍历entrySet()并找到与值匹配的键。这是最慢的方法,因为它需要遍历整个集合,而其他两个方法不需要这样做。

如果你在自己的代码中构建映射,尝试将map中的键和值放在一起:

public class KeyValue {
public Object key;
public Object value;
public KeyValue(Object key, Object value) { ... }
}


map.put(key, new KeyValue(key, value));

当你有了一个值,你也就有了键。

是的,您必须遍历hashmap,除非您按照这些不同答案所建议的方式实现某些东西。而不是摆弄entrySet,我只获得keySet(),遍历该集合,并保留为您提供匹配值的(第一个)键。如果你需要所有匹配这个值的键,显然你需要做所有的事情。

正如Jonas所建议的,这可能已经是containsValue方法正在做的事情,所以您可以完全跳过这个测试,而只是每次都进行迭代(或者编译器可能已经消除了冗余,谁知道呢)。

另外,相对于其他答案,如果你的反向映射看起来像

Map<Value, Set<Key>>

如果需要这种功能,您可以处理非唯一key->值映射(先解开它们)。这将把fine纳入人们建议的任何解决方案中,使用两张地图。

如果你的数据结构在键和值之间有多对一映射,你应该遍历条目并选择所有合适的键:

public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
Set<T> keys = new HashSet<T>();
for (Entry<T, E> entry : map.entrySet()) {
if (Objects.equals(value, entry.getValue())) {
keys.add(entry.getKey());
}
}
return keys;
}

对于一对一的关系,你可以返回第一个匹配的键:

public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
for (Entry<T, E> entry : map.entrySet()) {
if (Objects.equals(value, entry.getValue())) {
return entry.getKey();
}
}
return null;
}

在Java 8中:

public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
return map.entrySet()
.stream()
.filter(entry -> Objects.equals(entry.getValue(), value))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}

另外,对于Guava用户,BiMap可能是有用的。例如:

BiMap<Token, Character> tokenToChar =
ImmutableBiMap.of(Token.LEFT_BRACKET, '[', Token.LEFT_PARENTHESIS, '(');
Token token = tokenToChar.inverse().get('(');
Character c = tokenToChar.get(token);

恐怕你只能重复你的地图了。我能想到的最简短的:

Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String,String> entry = iter.next();
if (entry.getValue().equals(value_you_look_for)) {
String key_you_look_for = entry.getKey();
}
}

你可以使用下面的代码获取键值。

ArrayList valuesList = new ArrayList();
Set keySet = initalMap.keySet();
ArrayList keyList = new ArrayList(keySet);


for(int i = 0 ; i < keyList.size() ; i++ ) {
valuesList.add(initalMap.get(keyList.get(i)));
}


Collections.sort(valuesList);
Map finalMap = new TreeMap();
for(int i = 0 ; i < valuesList.size() ; i++ ) {
String value = (String) valuesList.get(i);


for( int j = 0 ; j < keyList.size() ; j++ ) {
if(initalMap.get(keyList.get(j)).equals(value)) {
finalMap.put(keyList.get(j),value);
}
}
}
System.out.println("fianl map ---------------------->  " + finalMap);
public class NewClass1 {


public static void main(String[] args) {
Map<Integer, String> testMap = new HashMap<Integer, String>();
testMap.put(10, "a");
testMap.put(20, "b");
testMap.put(30, "c");
testMap.put(40, "d");
for (Entry<Integer, String> entry : testMap.entrySet()) {
if (entry.getValue().equals("c")) {
System.out.println(entry.getKey());
}
}
}
}

一些额外的信息…可能对你有用

如果你的hashmap真的很大,上面的方法可能不太好。如果您的hashmap包含唯一键到唯一值的映射,您可以维护一个包含从值到键映射的hashmap。

也就是说你必须维护两个hashmap

1. Key to value


2. Value to key

在这种情况下,您可以使用第二个hashmap来获取key。

使用一个薄的包装:HMap

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


public class HMap<K, V> {


private final Map<K, Map<K, V>> map;


public HMap() {
map = new HashMap<K, Map<K, V>>();
}


public HMap(final int initialCapacity) {
map = new HashMap<K, Map<K, V>>(initialCapacity);
}


public boolean containsKey(final Object key) {
return map.containsKey(key);
}


public V get(final Object key) {
final Map<K, V> entry = map.get(key);
if (entry != null)
return entry.values().iterator().next();
return null;
}


public K getKey(final Object key) {
final Map<K, V> entry = map.get(key);
if (entry != null)
return entry.keySet().iterator().next();
return null;
}


public V put(final K key, final V value) {
final Map<K, V> entry = map
.put(key, Collections.singletonMap(key, value));
if (entry != null)
return entry.values().iterator().next();
return null;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


public class M{
public static void main(String[] args) {


HashMap<String, List<String>> resultHashMap = new HashMap<String, List<String>>();


Set<String> newKeyList = resultHashMap.keySet();




for (Iterator<String> iterator = originalHashMap.keySet().iterator(); iterator.hasNext();) {
String hashKey = (String) iterator.next();


if (!newKeyList.contains(originalHashMap.get(hashKey))) {
List<String> loArrayList = new ArrayList<String>();
loArrayList.add(hashKey);
resultHashMap.put(originalHashMap.get(hashKey), loArrayList);
} else {
List<String> loArrayList = resultHashMap.get(originalHashMap
.get(hashKey));
loArrayList.add(hashKey);
resultHashMap.put(originalHashMap.get(hashKey), loArrayList);
}
}


System.out.println("Original HashMap : " + originalHashMap);
System.out.println("Result HashMap : " + resultHashMap);
}
}
public static class SmartHashMap <T1 extends Object, T2 extends Object> {
public HashMap<T1, T2> keyValue;
public HashMap<T2, T1> valueKey;


public SmartHashMap(){
this.keyValue = new HashMap<T1, T2>();
this.valueKey = new HashMap<T2, T1>();
}


public void add(T1 key, T2 value){
this.keyValue.put(key, value);
this.valueKey.put(value, key);
}


public T2 getValue(T1 key){
return this.keyValue.get(key);
}


public T1 getKey(T2 value){
return this.valueKey.get(value);
}


}
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;


public class ValueKeysMap<K, V> extends HashMap <K,V>{
HashMap<V, Set<K>> ValueKeysMap = new HashMap<V, Set<K>>();


@Override
public boolean containsValue(Object value) {
return ValueKeysMap.containsKey(value);
}


@Override
public V put(K key, V value) {
if (containsValue(value)) {
Set<K> keys = ValueKeysMap.get(value);
keys.add(key);
} else {
Set<K> keys = new HashSet<K>();
keys.add(key);
ValueKeysMap.put(value, keys);
}
return super.put(key, value);
}


@Override
public 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;
}


public Set<K> getKeys4ThisValue(V value){
Set<K> keys = ValueKeysMap.get(value);
return keys;
}


public boolean valueContainsThisKey(K key, V value){
if (containsValue(value)) {
Set<K> keys = ValueKeysMap.get(value);
return keys.contains(key);
}
return false;
}


/*
* Take care of argument constructor and other api's like putAll
*/
}

您可以将键、值对及其逆函数插入到映射结构中

map.put("theKey", "theValue");
map.put("theValue", "theKey");

使用map.get("theValue")将返回"theKey"。

这是一种快速而肮脏的方法,我已经创建了常量映射,它只适用于少数数据集:

  • 只能包含1 ~ 1对
  • 值集与键集不相交(1-> 2,2 ->3中断它)
Iterator<Map.Entry<String,String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String,String> entry = iterator.next();
if (entry.getValue().equals(value_you_look_for)) {
String key_you_look_for = entry.getKey();
}
}

在java8

map.entrySet().stream().filter(entry -> entry.getValue().equals(value))
.forEach(entry -> System.out.println(entry.getKey()));

我认为这是最好的解决方案,原始地址:java2

    import java.util.HashMap;
import java.util.Map;


public class Main {


public static void main(String[] argv) {
Map<String, String> map = new HashMap<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 it
public static Object getKeyFromValue(Map hm, Object value) {
for (Object o : hm.keySet()) {
if (hm.get(o).equals(value)) {
return o;
}
}
return null;
}
}
一个简单的用法: 如果你把所有数据放在hasMap中,你有item = "Automobile",所以你在hashMap中寻找它的键。这是一个很好的解决方案。< / p >
getKeyFromValue(hashMap, item);
System.out.println("getKeyFromValue(hashMap, item): "+getKeyFromValue(hashMap, item));
/**
* This method gets the Key for the given Value
* @param paramName
* @return
*/
private String getKeyForValueFromMap(String paramName) {
String keyForValue = null;
if(paramName!=null)) {
Set<Entry<String,String>> entrySet = myMap().entrySet();
if(entrySet!=null && entrySet.size>0) {
for(Entry<String,String> entry : entrySet) {
if(entry!=null && paramName.equalsIgnoreCase(entry.getValue())) {
keyForValue = entry.getKey();
}
}
}
}
return keyForValue;
}

值得注意的是,由于这个问题,Apache Collections支持通用BidiMaps。因此,一些投票最多的答案在这一点上不再准确。

对于支持重复值的Serialized BidiMap(一对多的场景),也可以考虑MapDB.org

for(int key: hm.keySet()) {
if(hm.get(key).equals(value)) {
System.out.println(key);
}
}

使用Java 8:

ftw.forEach((key, value) -> {
if (value.equals("foo")) {
System.out.print(key);
}
});

面向API <的Android开发;19、Vitalii Fedorenko一对一关系解决方案不工作,因为Objects.equals没有实现。这里有一个简单的替代方案:

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();
}
}
return null;
}

用你自己的实现来装饰地图

class MyMap<K,V> extends HashMap<K, V>{


Map<V,K> reverseMap = new HashMap<V,K>();


@Override
public V put(K key, V value) {
// TODO Auto-generated method stub
reverseMap.put(value, key);
return super.put(key, value);
}


public K getKey(V value){
return reverseMap.get(value);
}
}

我的2美分。 您可以获取数组中的键,然后循环遍历数组。如果映射非常大,这将影响代码块的性能,因为首先获取数组中的键可能会消耗一些时间,然后再进行循环。否则,对于较小的地图,它应该是ok的

String[] keys =  yourMap.keySet().toArray(new String[0]);


for(int i = 0 ; i < keys.length ; i++){
//This is your key
String key = keys[i];


//This is your value
yourMap.get(key)
}
  1. 如果你想从value中获取key,最好使用bidimap(双向映射),你可以在O(1)时间内从value中获取key。

    但是,这样做的缺点是你只能使用唯一的keyset和valueset.

  2. 在java中有一个名为表格的数据结构,它只是像这样的映射的映射

    Table<A, B, C > == map <一、地图<B c > >

    这里你可以通过查询T.row(a);得到map<B,C>,也可以通过查询T.column(b);

    得到map<A,C>

在你的特殊情况下,插入C作为某个常数。

所以,它像<A1 b1 1 > & lt;A2, b2, 1 >,…

如果你通过t。row(a1)——>返回——>的map get keyset这个返回的map。

如果你需要找到键值,那么T.column(b2)——>返回map of——> get keyset of返回map。

与前一种情况相比的优点:

  1. 可以使用多个值。
  2. 在使用大型数据集时效率更高。

你可以使用下面的:

public class HashmapKeyExist {
public static void main(String[] args) {
HashMap<String, String> hmap = new HashMap<String, String>();
hmap.put("1", "Bala");
hmap.put("2", "Test");


Boolean cantain = hmap.containsValue("Bala");
if(hmap.containsKey("2") && hmap.containsValue("Test"))
{
System.out.println("Yes");
}
if(cantain == true)
{
System.out.println("Yes");
}


Set setkeys = hmap.keySet();
Iterator it = setkeys.iterator();


while(it.hasNext())
{
String key = (String) it.next();
if (hmap.get(key).equals("Bala"))
{
System.out.println(key);
}
}
}
}
public static String getKey(Map<String, Integer> mapref, String value) {
String key = "";
for (Map.Entry<String, Integer> map : mapref.entrySet()) {
if (map.getValue().toString().equals(value)) {
key = map.getKey();
}
}
return key;
}

我认为键盘()可以很好地找到映射到值的键,并且具有比entrySet ()更好的编码风格。

例:

假设你有一个HashMap 地图, ArrayList res,一个你想要找到所有的关键映射到的价值,然后存储到res的键。

你可以编写如下代码:

    for (int key : map.keySet()) {
if (map.get(key) == value) {
res.add(key);
}
}

而不是使用下面的entrySet():

    for (Map.Entry s : map.entrySet()) {
if ((int)s.getValue() == value) {
res.add((int)s.getKey());
}
}

希望能有所帮助。

虽然这并没有直接回答问题,但它是相关的。

这样你就不需要继续创建/迭代了。只需创建一个反向映射一次,就可以得到你需要的东西。

/**
* 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
*/
public static <K, V> Map<V, List<K>> reverseMap(Map<K,V> map) {
if(map == null) return null;


Map<V, List<K>> reverseMap = new ArrayMap<>();


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
*/
public static <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 = new ArrayList<>();
newList.add(value);
map.put(key, newList);
}
else {
list.add(value);
}


return map;
}

试试这个:

static String getKeyFromValue(LinkedHashMap<String, String> map,String value) {
for (int x=0;x<map.size();x++){
if( String.valueOf( (new ArrayList<String>(map.values())).get(x) ).equals(value))
return String.valueOf((new ArrayList<String>(map.keySet())).get(x));
}
return null;
}

据我所知,当你将HashMap的键和值表示为数组时,它们是不混合的:

hashmap.values().toArray()

而且

hashmap.keySet().toArray()

所以下面的代码(从java 8开始)应该像预期的那样工作:

public Object getKeyByFirstValue(Object value) {
int keyNumber =  Arrays.asList(hashmap.values().toArray()).indexOf(value);
return hashmap.keySet().toArray()[keyNumber];
}

然而,(警告!)它的工作速度比迭代慢2-3倍。

设值为maxValue

Set keySet = map.keySet();


keySet.stream().filter(x->map.get(x)==maxValue).forEach(x-> System.out.println(x));
< p > Lambda w/o使用外部库
可以处理一个键的多个值(与BidiMap不同)

public static List<String> getKeysByValue(Map<String, String> map, String value) {
List<String> list = map.keySet().stream()
.collect(groupingBy(k -> map.get(k))).get(value);
return (list == null ? Collections.emptyList() : list);
}

得到一个包含key(s)映射value
List 对于1:1映射,返回的列表为empty或包含1 value

让我们看看我的例子

Map<String, String> mapPeopleAndCountry = new HashMap<>();
mapPeopleAndCountry.put("Matis", "Lithuania");
mapPeopleAndCountry.put("Carlos", "Honduras");
mapPeopleAndCountry.put("Teboho", "Lesotho");
mapPeopleAndCountry.put("Marielos", "Honduras");




List<String> peopleInHonduras = mapPeopleAndCountry.keySet()
.stream()
.filter(r -> mapPeopleAndCountry.get(r)
.equals("Honduras"))
.stream(Collectors.toList());


// will return ["Carlos", "Marielos"]


注:未经测试,可能含有错别字

从Map中获取给定值的键的最简单实用方法:

public static void fetchValue(Map<String, Integer> map, Integer i)
{
Stream stream = map.entrySet().stream().filter(val-> val.getValue().equals(i)).map(Map.Entry::getKey);
stream.forEach(System.out::println);
}

详细解释:

  1. Method fetchValue接受映射,该映射以String为键,Integer为值。

  2. 然后我们使用entryset().stream()将结果转换为流。

  3. 接下来我们使用过滤器(中间操作),它给我们一个等于第二个参数的值。

  4. 最后,我们使用forEach(final操作)来打印最终结果。

找到了太多答案。有些真的很棒。但我特别在寻找一种方法,这样我就可以用循环得到值。

下面是我最后做的: 对于HashMap 1对1关系:

    Map<String, String> map = new HashMap<String, String>();
    

map.put("abc", "123");
map.put("xyz", "456");
    

for(Entry<String, String> entry : map.entrySet()) {
if(entry.getValue().equalsIgnoreCase("456")) {
System.out.println(entry.getKey());
}
}

输出:“xyz"

对于HashMap一对多关系:

    Map<String, ArrayList<String>> service = new HashMap<String, ArrayList<String>>();
    

service.put("abc", new ArrayList<String>());
service.get("abc").add("a");
service.get("abc").add("b");
service.get("abc").add("c");
    

service.put("xyz", new ArrayList<String>());
service.get("xyz").add("x");
service.get("xyz").add("y");
service.get("xyz").add("z");
    

for(Entry<String, ArrayList<String>> entry : service.entrySet()) {
ArrayList<String> values = entry.getValue();
for(String value : values) {
if(value.equalsIgnoreCase("x")) {
System.out.println(entry.getKey());
}
}
        

}

输出:xyz

-谢谢

你还可以这样做: 第一个:put map (key, value) 第二:更新键需要删除表达式 第三:用oldValue

放一个新键