Java Hashmap:如何从值获取密钥?

Nic*_*ner 429 java hashmap

如果我的价值"foo",以及HashMap<String> ftw对于其ftw.containsValue("foo")返回true,我怎么能得到相应的钥匙?我是否必须遍历hashmap?最好的方法是什么?

Vit*_*nko 593

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

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;
}
Run Code Online (Sandbox Code Playgroud)

如果是一对一关系,您可以返回第一个匹配的密钥:

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;
}
Run Code Online (Sandbox Code Playgroud)

在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());
}
Run Code Online (Sandbox Code Playgroud)

此外,对于Guava用户,BiMap可能很有用.例如:

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

  • 你能说一下性能吗?什么会更优化?这个还是BidiMap? (3认同)
  • 我建议用`.filter(entry - >`[`Objects.equals`](http://docs.oracle.com/)替换`.filter(entry - > entry.getValue().equals(value))` javase/8/docs/api/java/util/Objects.html #equals-java.lang.Object-java.lang.Object-)`(entry.getValue(),value))`因为没有关于`null的声明能力得到了.此外,您可以将`.map(entry - > entry.getKey())`替换为`.map(Map.Entry :: getKey)` (3认同)
  • http://stackoverflow.com/questions/4553624/hashmap-get-put-complexity HashMap具有时间复杂度o(1)。如果您遍历这些值,那么它将破坏性能。如果您想要“更好的性能”并且具有“一对一”的关系,则可以使用“值是关键”的“另一张地图”。 (2认同)

Vin*_*lds 208

如果您选择使用Commons Collections库而不是标准Java Collections API,则可以轻松实现此目的.

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

但有一点需要注意,比迪映射不能将多个值映射到键,因此除非您的数据集在键和值之间具有1:1的映射,否则您无法使用bidimaps.

更新

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

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

更新#2

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

  • ...如果你喜欢Generics和所有现代的东西,Google Collections有BiMap,你可以通过调用biMap.inverse()得到键匹配的指定值.get(value); (21认同)
  • @whiskeysierra:我认为没有人(目前)这么说. (12认同)
  • Google Collections**不是**Commons-Collections的重构版本. (2认同)
  • Apache Collections现在支持泛型https://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/bidimap/package-summary.html (2认同)

Fat*_*n P 73

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());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

那就是你必须保持两个哈希图

1. Key to value

2. Value to key 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您可以使用第二个hashmap来获取密钥.


Chi*_*Chi 19

我认为你的选择是

  • 使用为此构建的地图实现,例如google集合中的BiMap.请注意,Google集合BiMap需要无关的值以及键,但它可以在两个方向上提供高性能的性能
  • 手动维护两个映射 - 一个用于键 - >值,另一个映射用于值 - >键
  • 迭代entrySet()并找到与值匹配的键.这是最慢的方法,因为它需要遍历整个集合,而其他两种方法不需要这样.


Chi*_*itz 18

您可以将键,值对及其反转插入到地图结构中

map.put("theKey", "theValue");
map.put("theValue", "theKey");
Run Code Online (Sandbox Code Playgroud)

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

这是一种快速而肮脏的方式,我制作了恒定的地图,这只适用于少数几个数据集:

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

  • 这不是真的正确.这不仅需要1-1,而且该组值与该组键不相交.你不能将它应用于双射映射{1 - > 2,2 - > 3}:2既是值又是键. (4认同)

小智 15

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


rec*_*ive 11

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


wso*_*son 11

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

  • 这个解决方案非常密集,在大型HashMaps上是不切实际的. (3认同)

Dav*_*ker 9

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

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

map.put(key, new KeyValue(key, value));
Run Code Online (Sandbox Code Playgroud)

然后当你有一个值时,你也有了钥匙.

  • 聪明,但是如果有2个或更多KeyValue对象包含相同的值呢?应该选择哪个键? (3认同)
  • @Vineet,我不知道这种方法如何解决OP的问题.你是什​​么意思"当你有价值时,你也有钥匙."? (2认同)

小智 9

我认为这是最好的解决方案,原始地址: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;
          }
        }
Run Code Online (Sandbox Code Playgroud)

一个简单的用法:如果您将所有数据放在hasMap中并且您有item ="Automobile",那么您在hashMap中查找其关键字.这是很好的解决方案.

getKeyFromValue(hashMap, item);
System.out.println("getKeyFromValue(hashMap, item): "+getKeyFromValue(hashMap, item));
Run Code Online (Sandbox Code Playgroud)


pha*_*ani 7

使用Java 8:

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

  • `value ==“ foo”`这将不起作用。“等于”应该用于比较字符串。 (3认同)

Jon*_*ing 6

这听起来是最好的方式,你可以迭代使用条目,map.entrySet()因为map.containsValue()无论如何可能会这样做.


小智 6

我担心你只需要迭代你的地图.最短的我可以想出:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

for(int key: hm.keySet()) {
    if(hm.get(key).equals(value)) {
        System.out.println(key); 
    }
}
Run Code Online (Sandbox Code Playgroud)


The*_*rga 5

对于针对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;
}
Run Code Online (Sandbox Code Playgroud)