繁体   English   中英

Java Hashmap:如何从值中获取键?

[英]Java Hashmap: How to get key from value?

如果我有值"foo"和一个HashMap<String> ftw ftw.containsValue("foo")返回true ,我怎样才能获得相应的键? 我必须遍历哈希图吗? 最好的方法是什么?

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

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);

如果您选择使用Commons Collections 库而不是标准的 Java Collections 框架,则可以轻松实现这一点。

Collections 库中的BidiMap接口是一个双向映射,允许您将键映射到值(如法线映射),也可以将值映射到键,从而允许您在两个方向上执行查找。 getKey()方法支持获取值的键。

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


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

一旦可以确保,使用entrySet()方法获取 Map 中的条目集(映射)。 获得类型为Map.Entry的集合后,遍历条目,将存储的值与预期进行比较,并获得相应的 key


可以在Google Guava和重构的Commons-Collections库(后者不是 Apache 项目)中找到对具有泛型的双向映射的支持。 感谢 Esko 指出 Apache Commons Collections 中缺少的通用支持。 使用带有泛型的集合可以使代码更易于维护。


4.0 版开始,官方 Apache Commons Collections™ 库支持泛型

有关现在支持 Java泛型BidiMapOrderedBidiMapSortedBidiMap接口的可用实现列表,请参阅“ org.apache.commons.collections4.bidimap ”包的摘要页面。

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

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

如果您的哈希图非常大,则上述方法可能并不好。 如果您的哈希图包含唯一键到唯一值的映射,您可以再维护一个包含从值到键的映射的哈希图。

那就是你必须维护两个哈希图

1. Key to value

2. Value to key 

在这种情况下,您可以使用第二个哈希图来获取密钥。

您可以将键值对及其逆插入到您的地图结构中

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

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

这是我制作常量映射的一种快速而肮脏的方式,它仅适用于选定的几个数据集:

  • 仅包含 1 到 1 对
  • 值集与键集不相交(1->2, 2->3 打破它)

我认为你的选择是

  • 使用为此构建的地图实现,例如来自谷歌集合的BiMap 请注意,谷歌集合 BiMap 需要唯一的值和键,但它提供了双向性能的高性能
  • 手动维护两个映射 - 一个用于键 -> 值,另一个映射用于值 -> 键
  • 遍历entrySet()并找到与值匹配的键。 这是最慢的方法,因为它需要遍历整个集合,而其他两种方法不需要。

使用 Java 8:

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

使用您自己的实现装饰地图

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);
    }
}

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

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

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

    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 中查找它的键。 这是一个很好的解决方案。

getKeyFromValue(hashMap, item);
System.out.println("getKeyFromValue(hashMap, item): "+getKeyFromValue(hashMap, item));

如果您在自己的代码中构建地图,请尝试将地图中的键和值放在一起:

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

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

然后,当您拥有价值时,您也拥有了钥匙。

恐怕你只需要迭代你的地图。 我能想到的最短时间:

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();
    }
}
for(int key: hm.keySet()) {
    if(hm.get(key).equals(value)) {
        System.out.println(key); 
    }
}

听起来最好的方法是使用map.entrySet()迭代条目,因为map.containsValue()可能无论如何都map.containsValue()

对于面向 API < 19 的 Android 开发,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;
}

您可以使用以下内容:

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);
            }
        }
    }
}

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

前任:

假设你有一个HashMap的地图,ArrayList的资源,要找到所有的键映射到一个,然后将密钥存储到水库

您可以在下面编写代码:

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

希望能帮助到你 :)

是的,您必须遍历哈希图,除非您按照这些不同答案所建议的方式实现某些内容。 而不是摆弄 entrySet,我只是获取 keySet(),迭代该集合,并保留获得匹配值的(第一个)键。 如果您需要与该值匹配的所有键,显然您必须做整件事。

正如 Jonas 所建议的,这可能已经是 containsValue 方法正在执行的操作,因此您可以一起跳过该测试,并且每次都进行迭代(或者编译器可能已经消除了冗余,谁知道呢)。

另外,相对于其他答案,如果您的反向地图看起来像

Map<Value, Set<Key>>

如果您需要该功能(将它们解开),您可以处理非唯一键-> 值映射。 这将很好地融入人们在这里使用两张地图建议的任何解决方案中。

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);
    }

}

在java8中

map.entrySet().stream().filter(entry -> entry.getValue().equals(value))
    .forEach(entry -> System.out.println(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 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;
}
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);
    }
}
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
     */
}
/**
 * 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;
}

我的 2 美分。 您可以获取数组中的键,然后遍历数组。 如果地图非常大,这将影响此代码块的性能,其中您首先在数组中获取键,这可能会消耗一些时间,然后您正在循环。 否则对于较小的地图应该没问题。

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)            
}

虽然这不能直接回答问题,但它是相关的。

这样你就不需要继续创建/迭代。 只需创建一次反向地图即可获得所需内容。

/**
 * 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;
}

从 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. 方法 fetchValue 接受映射,它以 String 为键,以 Integer 为值。

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

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

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

使用薄包装: 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;
   }
}

重要的是要注意,由于这个问题,Apache Collections 支持Generic BidiMaps 因此,在这一点上,一些投票最高的答案不再准确。

对于还支持重复值(一对多场景)的序列化 BidiMap,还可以考虑MapDB.org

如果我的值是"foo" ,并且ftw.containsValue("foo")返回trueHashMap<String> ftw ,如何获得相应的键? 我是否必须遍历哈希图? 最好的方法是什么?

试试这个:

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));

不使用外部库的 lambda
可以为一个key处理多个值(与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);
}

得到一个List包含key(s)的映射value
对于 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"]

注意:未经测试,可能包含错字

找到了太多答案。 有些真的很棒。 但我特别在寻找一种方法,以便我可以使用循环获取值。

所以这终于是我所做的:对于 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

-谢谢

你也可以做这个工作:第一:放置映射(键,值)第二:更新你需要删除表达式的键第三:并用 oldValue 放置一个新键

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM