"foo"
和HashMap<String> ftw
返回ftw.containsValue("foo")
的true
,如何获得相应的密钥?我是否必须遍历哈希图?最好的方法是什么?#1 楼
如果您选择使用Commons Collections库而不是标准Java Collections框架,则可以轻松实现。Collections库中的
BidiMap
接口是双向映射,允许您将密钥映射到值(如法线贴图),还可以将值映射到键,从而允许您在两个方向上执行查找。 getKey()
方法支持获取值的键。需要注意的是,比迪地图不能将多个值映射到键,因此除非您的数据集在键和值之间具有1:1映射,否则无法使用比迪地图。
更新#1
如果要依赖Java Collections API,则在插入时必须确保键和值之间的1:1关系将值放入地图。说起来容易做起来难。
一旦可以确保使用
entrySet()
方法来获取Map中的一组条目(映射)。一旦获得类型为Map.Entry
的集合,就循环访问这些条目,将存储的值与期望值进行比较,并获得相应的键。更新#2
对带比迪地图的支持泛型可以在Google Guava和重构的Commons-Collections库中找到(后者不是Apache项目)。感谢Esko指出Apache Commons Collections中缺少的通用支持。将集合与泛型一起使用可以使代码更易于维护。
更新#3
从4.0版开始,正式的Apache Commons Collections™库支持泛型。
请参见“组织的摘要页面。 apache.commons.collections4.bidimap“软件包中的
BidiMap
,OrderedBidiMap
和SortedBidiMap
接口的可用实现的列表,这些接口现在支持Java泛型。评论
...并且,如果您喜欢泛型和所有现代的东西,则Google Collections提供了BiMap,您可以在其中通过调用biMap.inverse()。get(value)获得与指定值匹配的键。
– Esko
09年9月5日在20:15
是的,Apache Commons Collections不支持泛型。但是,正如您所指出的,这里有Google Collections(我还没有使用-尚无1.0版本),并且有重构的Commons-Collections支持Generics。您将在Sourceforge项目中找到它@ sourceforge.net/projects/collections
–威诺雷诺兹
09年9月5日在20:29
Google收藏夹不是Commons-Collections的重构版本。
– Whiskeysierra
2010年8月9日在16:42
@whiskeysierra:我认为目前没有人这么说。
–哈夫
2011年9月29日3:00在
Apache Collections现在支持泛型commons.apache.org/proper/commons-collections/javadocs/…
– kervin
15年5月31日在16:28
#2 楼
如果您的数据结构在键和值之间具有多对一的映射,则应遍历条目并选择所有合适的键: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);
评论
您能谈谈演出吗?什么会更优化?这还是BidiMap?
–塔斯马尼亚克
13年8月27日在13:41
我曾想过相同的解决方案,我当然赞成它,但是对于真正的大型馆藏,我怀疑它的效率。
– arjacsoh
2013年9月10日下午6:59
stackoverflow.com/questions/4553624/hashmap-get-put-complexity HashMap的时间复杂度为o(1)。如果要遍历这些值,那么它将破坏性能。如果您想获得更好的性能并具有一对一的关系,则可以使用另一个以价值为关键的地图
–veer7
2015年4月9日在10:03
我建议将.filter(entry-> entry.getValue()。equals(value))替换为.filter(entry-> Objects.equals(entry.getValue(),value)),因为没有关于可空性的声明。此外,您可以将.map(entry-> entry.getKey())替换为.map(Map.Entry :: getKey)
–霍尔格
2015年4月21日在9:37
我在理解Set
–ponderingdev
16 Dec 23'5:50
#3 楼
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
在这种情况下,您可以使用第二个哈希图来获取密钥。
#4 楼
您可以将键,值对及其相反值插入地图结构中。map.put("theKey", "theValue");
map.put("theValue", "theKey");
使用map.get(“ theValue”)将返回“ theKey”。 br />
这是我制作常量映射的一种快速而肮脏的方法,它仅适用于少数几个数据集:
仅包含1对1
值集与键集不相交(1-> 2,2-> 3断开键)
评论
这不是真的正确。这不仅需要1-1,而且值的集合与键的集合是不相交的。您不能将其应用于双射映射{1-> 2,2-> 3}:2既是值又是键。
–路易斯·弗洛里特(Luis A. Florit)
16-2-4在0:30
#5 楼
我认为您的选择是使用为此目的构建的地图实现,例如Google馆藏的BiMap。请注意,Google集合BiMap要求值和键都必须是唯一的,但它在两个方向上都具有很高的性能。
手动维护两个映射-一个用于键->值,另一个用于值->键< br遍历
entrySet()
并找到与该值匹配的键。这是最慢的方法,因为它需要遍历整个集合,而其他两种方法则不需要。#6 楼
使用自己的实现装饰地图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);
}
}
评论
我认为这是一种有趣的方法,尽管由于关系必须为1:1,所以我将完全放弃HashMap并改为实现Map
– Fran Marzoa
16-10-11在9:21
#7 楼
没有明确的答案,因为多个键可以映射到相同的值。如果要使用自己的代码强制唯一性,则最佳解决方案是创建一个使用两个Hashmap来跟踪两个方向上的映射的类。#8 楼
要查找映射到该值的所有键,请使用map.entrySet()
遍历哈希图中的所有对。评论
这种解决方案非常费力,以至于在大型HashMap上不切实际。
–Joehot200
2015年3月10日在12:58
#9 楼
使用Java 8:ftw.forEach((key, value) -> {
if (value.equals("foo")) {
System.out.print(key);
}
});
评论
value ==“ foo”这将不起作用。 equals应该用于比较字符串。
–安东·巴拉纽克(Anton Balaniuc)
18年5月8日在14:10
@Anton,除非值已被内插,否则为true。
– frododot
18年11月14日在4:16
#10 楼
我认为这是最好的解决方案,原始地址为: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));
#11 楼
如果您使用自己的代码构建地图,请尝试将键和值放在地图中:public class KeyValue {
public Object key;
public Object value;
public KeyValue(Object key, Object value) { ... }
}
map.put(key, new KeyValue(key, value));
然后,当您有值时,您也有键。
评论
聪明,但是如果有两个或更多个包含相同值的KeyValue对象怎么办?应该选择哪个键?
–威诺雷诺兹
09年9月5日在18:15
@Vineet,我不知道这种方法如何解决OP的问题。您的意思是“然后,当您拥有价值时,您也拥有钥匙”。
–李强
2011年9月4日下午16:53
#12 楼
恐怕您只需要迭代地图即可。最短的时间我想出了: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();
}
}
#13 楼
for(int key: hm.keySet()) {
if(hm.get(key).equals(value)) {
System.out.println(key);
}
}
#14 楼
听起来最好的方法是使用map.entrySet()
遍历条目,因为map.containsValue()
可能还是这样做。评论
是的,这就是它的作用。但是,当然,一旦找到一个等于.equals的值,而不是OP可能需要执行的操作,它就会返回true。
–CPerkins
09年9月6日在0:08
好吧,迭代条目也可以在找到匹配值后立即以key返回。似乎没有多场比赛。
–乔纳斯(Jonas K)
09年9月6日在22:11
#15 楼
对于面向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;
}
评论
这个解决方案对我有用;我还开发了一个考古Android版本,以获取“ onMarkerClick”事件中保存在地图中的Google Map标记的密钥为例。迭代entrySet起作用;但是没有迭代键,并使用get()将它们与条目匹配,然后比较输出。
– Toby Wilson
16 Dec 24'在18:02
#16 楼
您可以使用以下内容: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);
}
}
}
}
评论
您很想提供有用的东西,但是它不应该是“仅代码”的答案,并且代码本身也不应该充满代码的味道。
–汤姆
16年11月9日23:17
#17 楼
是的,除非您按照这些不同答案所建议的方式实施某些操作,否则您必须遍历哈希图。我只需要获取keySet(),然后遍历该集合,然后保留(第一个)键即可获得您的匹配值,而无需摆弄entrySet。如果您需要与该值匹配的所有键,显然您必须做整个事情。正如乔纳斯(Jonas)所建议的,这可能已经是containsValue方法正在执行的操作,因此您可以跳过该测试全部在一起,并且每次都进行迭代(否则编译器将已经消除了冗余,谁知道)。
相对于其他答案,如果您的反向映射看起来像
Map<Value, Set<Key>>
,如果需要此功能,可以处理非唯一键->值映射(将它们解开)。这可以很好地结合到人们使用两个地图在这里建议的任何解决方案中。
#18 楼
您可以使用以下代码使用值获取密钥。.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);
#19 楼
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);
}
}
评论
我认为可以通过添加解释来改善此答案。
–乔纳森
2012年12月12日20:47
-1。我用String作为键和值对其进行了测试。当我调用map.add(“ 1”,“ 2”); map.add(“ 1”,“ 3”);然后我可以调用map.getKey(“ 2”);并检索“ 1”,即使“ 1”是“ 3”的键也是如此。
– jlordo
13年1月30日在10:01
@Jonathan此类的想法是保留另一个具有反向映射的HashMap,以便除了从键检索值之外,还可以从值检索键。 T1和T2类有点让人困惑。也许从字面上将其命名为“键与值”?尽管我希望能够获得一个以上的值或一个以上的键作为回报,这取决于数据和所需内容。谨慎使用
–乔科维茨
2014年10月6日20:54
@theknightwhosaysni“ 1”不再是“ 2”的键(不再)。这也是您问题的答案,调用getValue(“ 1”)将返回3。
– jlordo
14-10-25在11:24
抱歉,我很误会我对标准Hashmap行为的误解:您是正确的,因为为键添加新值应该替换旧值
–乔科维茨
14-10-27在4:26
#20 楼
在java8中map.entrySet().stream().filter(entry -> entry.getValue().equals(value))
.forEach(entry -> System.out.println(entry.getKey()));
#21 楼
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;
}
评论
Map
–神奇的印度
17年6月22日在7:35
如果多个键具有相同的值会怎样?
–Càphêđen
17年6月22日在7:45
当u传递时,多个键具有相同的值,我们将得到最后一个键作为结果。示例:A 1,B 1,C 1,D 2输出:如果我们传递1值,则输出将为C
–神奇的印度
17年6月22日在9:39
@AmazingIndia无法保证,它完全取决于特定的地图实现。例如,HashMap不能保证订单,因此您不知道将在此处返回什么输出。
– Niels Doucet
17年8月16日在8:34
#22 楼
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
*/
}
#23 楼
/**
* 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;
}
#24 楼
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);
}
}
#25 楼
使用薄包装器:HMapimport 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;
}
}
#26 楼
我的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)
}
评论
为什么有人要使用这种方法?正如您已经说过的那样,与其他方法相比,性能会更差。
–汤姆
16年7月5日在13:06
#27 楼
我认为keySet()可能很适合查找映射到该值的键,并且比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());
}
}
希望对您有所帮助:)
评论
当比较对象时,检查对象的相等性时,map.get(key)== value不是一个好主意。对象相等性应始终使用其.equals()
– frododot
18-11-14在4:10
#28 楼
虽然这不能直接回答问题,但它是相关的。这样,您无需继续创建/迭代。只需创建一次反向映射即可获得所需的内容。
/**
* 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;
}
#29 楼
值得注意的是,由于这个问题,Apache Collections支持Generic BidiMaps。因此,一些投票最多的答案在这一点上不再是准确的。对于还支持重复值(一对多情况)的序列化BidiMap,也可以考虑使用MapDB.org。
#30 楼
如果要从值中获取密钥,最好使用双向地图(bidimap),则可以在O(1)时间内从值中获取密钥。
但是,这样做的缺点是您只能使用唯一的键集和值集。
java中有一个名为Table的数据结构,它只不过是像
表 ==映射>
这里您可以通过查询
map<B,C>
来获取T.row(a);
,也可以通过查询来获取map<A,C>
T.column(b);
在特殊情况下,将C插入为常量。
因此,它类似于
因此,如果您通过T.row(a1)查找--->返回->的映射,请获取此返回映射的键集。
如果需要查找键值,则T.column(b2)->返回->的映射,获取返回的映射的键集。
前一种情况的优点:
可以使用多个值。
使用大型数据集时效率更高。
评论
请注意,没有单个对应的键-可能有多个键映射到相同的值。@CPerkins,但对于像
如果您有少量物品,请考虑创建常量public static final String TIME =“ time”;和properties.put(TIME,PbActivityJpa_.time);