below is my code...
Map<Integer, String> MyType = sessionInfo.getType();
//{2=somename}
I am trying to get key from value...without running any loops....is it possible?
MyType.get("somename") // should output 2`
It's not easy to get key from value in Hashtable or HashMap, as compared to getting value from key, because Hash Map or Hashtable doesn't enforce one to one mapping between key and value inside Map in Java. infact Map allows same value to be mapped against multiple keys inside HashMap, Hashtable or any other Map implementation.
String key= null;
String value="somename";
for(Map.Entry entry: MyType.entrySet()){
if(value.equals(entry.getValue())){
key = entry.getKey();
break; //breaking because its one to one map
}
}
I would encourage running a loop for simplicity. It most likely will not slow down your program a noticeable amount.
However, if you must not run a loop, Google's Guava library has a BiDirectional Map Collection called BiMap that can be (found here). The map works both ways and is guaranteed to be synchronized at all times. I also am assuming that you have unique values in your map. If you do not, duplicate values will not have a specific key to link to.
BiMap<String, Integer> biMapInversed = biMap.inverse(); // how to get inverted map
Again, I wouldn't encourage this unless absolutely necessary. Looping through will work perfectly fine in most cases.
Taken from this SO answer
If you choose to use the Commons Collections library instead of
the standard Java Collections API, you can achieve this with ease.
The BidiMap interface in the Collections library is a
bi-directional map, allowing you to map a key to a value (like normal
maps), and also to map a value to a key, thus allowing you to perform
lookups in both directions. Obtaining a key for a value is supported
by the getKey() method.
There is a caveat though, bidi maps cannot have multiple values mapped
to keys, and hence unless your data set has 1:1 mappings between keys
and values, you cannot use bidimaps.
This is not possible. You need to consider the value may be duplicated in map.
Ex, How do you deal with {2=somename} and {5=somename}
You still need to use a for loop to check value and get key and decide to break or go on when value is matched.
If you're sure that your values are unique you can iterate over the entries of your old map .
Map<String, Character> myNewHashMap = new HashMap<>();
for(Map.Entry<Character, String> entry : myHashMap.entrySet()){
myNewHashMap.put(entry.getValue(), entry.getKey());
}
Alternatively, you can use a Bi-Directional map like Guava provides and use the inverse() method :
BiMap<Character, String> myBiMap = HashBiMap.create();
myBiMap.put('a', "test one");
myBiMap.put('b', "test two");
BiMap<String, Character> myBiMapInversed = myBiMap.inverse();
Related
I am currently working on Data Structures for writing a program on encryption and decryption of names. I have a doubt in Map interface. Actually to get the value associated with a key we have get() method in Map interface. But how to retrieve the key of a particular value without iterating through all the key value pairs in Map interface
Thank you
As others have said, it can't be done. The Map interface and its implementations do not support that.
Consider using a BiMap such as the one inculed in Google Guava Collections. It establishes a one-to-one (bidirectional) relationship between keys and values.
https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap
Using BiMap you can use Key key = biMap.inverse().get(value) to get a key for a given value.
Given that values are unique, you could to it like this:
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
String key = map.entrySet().stream().
collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))
.get("value1");
System.out.println(key); //gives key1
how to retrieve the key of a particular value without iterating through all the key value pairs in Map interface
Key is Key, not the value. You cannot do it. That's not how Map implemented.
Even If you make it with some magic (iterating multiple times, checking for equls etc ..), that's not guaranteed to give expected result..
And as per the definition of Map, Key is unique not the value. So there will be duplicated values and when you get by value, which associated key you will expect to get ?
If you are sure that there are no duplicates, you can do
for (Entry<Integer, String> entry : testMap.entrySet()) {
if (entry.getValue().equals("c")) {
System.out.println(entry.getKey());
}
}
Well like everyone said, you can't really do it in a decent way because there can be duplicate values. You could search for a hit using the equals() method and compare values. but then again, why are you even using a key/value map if you wanted to do such a thing.
in short, you could write your own version of the get method, taking in a instance of the value object you are trying to get. But there really is not a point in using a map if you are going to do that.
Why not use a list of some sorts of you desire to search on value ?
You can not do that because the "values" can be duplicated.
As said, that is not provided by Java-Map interface, since you should have a key and get the value then.
Usually you have something like this.
User user = ...;
HashMap<String, User> usernamesToUser = ...
Then you can get the key by something like:
String username = user.getUsername();
So without using the map actually.
However what you can do is, if the key is not directly to retrieve from the object you can use two Maps for both directions. So consider former example (just assume User the User object does not safe the Username)
Map<User, String> userMapReverse = ....;
Map<String, User> userMap = ....;
String username = userMapReverse.get(user);
However this option requires that you maintain two maps, which can be pretty ugly sometimes.
This question is kind of already posted here:
How to convert Map<String, String> to Map<Long, String> using guava
I think the answer of CollinD is appropriate:
All of Guava's methods for transforming and filtering produce lazy
results... the function/predicate is only applied when needed as the
object is used. They don't create copies. Because of that, though, a
transformation can easily break the requirements of a Set.
Let's say, for example, you have a Map<String, String> that contains
both "1" and "01" as keys. They are both distinct Strings, and so the
Map can legally contain both as keys. If you transform them using
Long.valueOf(String), though, they both map to the value 1. They are
no longer distinct keys. This isn't going to break anything if you
create a copy of the map and add the entries, because any duplicate
keys will overwrite the previous entry for that key. A lazily
transformed Map, though, would have no way of enforcing unique keys
and would therefore break the contract of a Map.
This is true, but actually I don't understand why it is not done because:
When the key transformation happen, if 2 keys are "merged", a runtime exception could be raised, or we could pass a flag to indicate to Guava to take any value of the multiple possible values for the newly computed key (failfast/failsafe possibilities)
We could have a Maps.transformKeys which produces a Multimap
Is there a drawback I don't see in doing such things?
As #CollinD suggests, there's no way to do this in a lazy way. To implement get, you have to convert all the keys with your transformation function (to ensure any duplicates are discovered).
So applying Function<K,NewK> to Map<K,V> is out.
You could safely apply Function<NewK,K> to the map:
V value = innerMap.get( fn.apply(newK) );
I don't see a Guava shorthand for that--it may just not be useful enough. You could get similar results with:
Function<NewK,V> newFn = Functions.compose(Functions.forMap(map), fn);
I am using a concurrent hashmap of structure
Map<Set<Date>, Map<String, Object>> SampleMap
The Map used inside the given map (Map<String, Object>) is also a concurrent hashmap,
but set is a only TreeSet type.
Still I get concurrent Modification exception when I add following line in logs,
logger.debug("sampleMap.keySet() + ". Size is " + sampleMap.keySet().size()");
and also in some other parts of same class dealing with this map.
This map is extensively used in Batch process by multiple threads to put and remove values in map and java version used is 1.5.
I think the exception is due to Treeset and also i find there is no similar implementation of concurrent handling collection for type Set.
It would be great if any one confirm whether my thinking over given issue is correct and also please suggest solution for this problem?
Since you need to be able to "modify" the key, you need to follow this pattern
// lock the collection
Map<String, Object> values = map.remove(key);
key = new TreeSet<String>(key);
// modify copy of key
map.put(key, values);
// unlock the collection.
As you are performing an operation which ConcurrentMap does not support, you have to use your own locking. You can use a plain HashMap or LinkedHashMap with synchronized or ReentrantReadWriteLock.
You can create a Concurrent set using
// Added in Java 1.6
Set<String> set = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
// or to be sorted
Set<String> set = Collections.newSetFromMap(new ConcurrentSkipListMap<String, Boolean>());
However, you can't change the contents of a key so what you should be using is
Set<String> key = Collections.unmodifiableSet(treeSet);
// or to be sure its not modified
Set<String> key = Collections.unmodifiableSet(new TreeSet<String>(treeSet));
A simple example of why you cannot change a key after using it in a Map.
Set<String> key1 = new TreeSet<String>();
Map<Set<String>, Boolean> map = new ConcurrentHashMap<Set<String>, Boolean>();
map.put(key1, true);
System.out.println("Is the map ok? "+map.containsKey(key1));
key1.add("hello");
System.out.println("Is the map ok? "+map.containsKey(key1));
prints
Is the map ok? true
Is the map ok? false
The common behaviour is that it can no longer see the key in the map. This is because the map places the key into a bucket based on its hashCode. If the hashCode changes, it can be in the wrong bucket so when it looks for it, it can't find it.
Is there any way to get a key associated with a known value in a map? Normally you know the key and you want to get the value, but I want to do the opposite, going from value to key. Is it possible?
Yes, you have to iterate over the values in the map and then store each key in a list:
for (Map.Entry<K,V> entry : map.entrySet()) {
V value = entry.getValue();
if (value.equals(someTargetValue) {
// add key (entry.getKey()) to list
}
}
Or you could use a bidirectional map, though do note:
This map enforces the restriction that there is a 1:1 relation between keys and values, meaning that multiple keys cannot map to the same value.
Well, I am not an expert on Google Project LambdaJ, but it certainly offers a few cool alternatives.
Supposing you have a map with all the days of the month:
month.put(1,"Monday");
month.put(2,"Tuesday");
month.put(3,"Wednesday");
...
Then we could easily achieve what you want like this:
Set<Integer> result = with(month).retainValues(is("Friday")).keySet();
Or even a few more interesting searches like:
Set<Integer> result = with(month).retainValues(anyOf(is("Monday"),is("Friday"))).keySet();
Without iterating all the keys looking for the value you can use an Apache Commons BidiMap
A Map is a mathematical entry which doesn't imply that a reverse mapping is possible. That said you might be able to create a "reverse" mapping if every mapped value is unique. Naturally, you'll have to encapsulate all the data manipulations in methods that update both Maps appropriately.
Map<Key, Value> normal;
Map<Value, Key> reverse;
If every mapped value is not unique, then you need to create a reverse mapping of a value to a list of keys.
Map<Key, Value> normal;
Map<Value, List<Key>> reverse;
Finally, if you don't care about fast access, you can iterate over the entire Map looking for Values. Since you'll need both the Value and the Key, it is probably best to iterate over the Map.Entry items.
Value searchingFor = ...;
Map<Key, Value> normal;
List<Key> keys = new ArrayList<Key>();
for (Map.Entry<Key, Value> entry : normal.entrySet()) {
if (entry.getValue().equals(searchingFor)) {
keys.add(entry.getKey());
}
}
The technique you choose to use will depend heavily on whether it is better to trade speed for memory footprint. Generally having an extra Map is faster due to the hashing being done on the Value(s), but costs extra memory. Having a loop over the Map.Entry(s) is slower, but costs less memory.
Here they already talked about Bidirectional maps. Nowadays, Guava (https://github.com/google/guava) offers a nice BiMap that you can use for that purpose:
https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap
Is it possible to have multiple values for the same key in a hash table? If not, can you suggest any such class or interface which could be used?
No. That's kind of the idea of hash tables.
However, you could either roll your own with a Map<YourKeyObject, List<YourValueObject>> and some utility methods for creating the list if it's not present, or use something like the Multimap from Google Collections.
Example:
String key = "hello";
Multimap<String, Integer> myMap = HashMultimap.create();
myMap.put(key, 1);
myMap.put(key, 5000);
System.out.println(myMap.get(key)); // prints either "[1, 5000]" or "[5000, 1]"
myMap = ArrayListMultimap.create();
myMap.put(key, 1);
myMap.put(key, 5000);
System.out.println(myMap.get(key)); // always prints "[1, 5000]"
Note that Multimap is not an exact equivalent of the home-baked solution; Hashtable synchronizes all its methods, while Multimap makes no such guarantee. This means that using a Multimap may cause you problems if you are using it on multiple threads. If your map is used only on one thread, it will make no difference (and you should have been using HashMap instead of Hashtable anyway).
Values of a hash table is Object so you can store a List
In a hashtable, one would use a key/value pair to store information.
In Java, the Hashtable class accepts a single value for a single key. The following is an example of an attempt to associate multiple values to a single key:
Hashtable<String, String> ht = new Hashtable<String, String>();
ht.put("Answer", "42");
ht.put("Hello", "World"); // First value association for "Hello" key.
ht.put("Hello", "Mom"); // Second value association for "Hello" key.
for (Map.Entry<String, String> e : ht.entrySet()) {
System.out.println(e);
}
In an attempt to include multiple values ("World", "Mom") to a single key ("Hello"), we end up with the following result for printing the entries in the Hashtable:
Answer=42
Hello=Mom
The key/value pair of "Hello" and "World" is not in the Hashtable -- only the second "Hello" and "Mom" entry is in the Hashtable. This shows that one cannot have multiple values associate with a single key in a Hashtable.
What is really needed here is a multimap, which allows an association of multiple values to a single key.
One implementation of the multimap is Multimap from Google Collections:
Multimap<String, String> mm = HashMultimap.create();
mm.put("Answer", "42");
mm.put("Hello", "World");
mm.put("Hello", "Mom");
for (Map.Entry<String, String> e : mm.entries()) {
System.out.println(e);
}
This is similar to the example above which used Hashtable, but the behavior is quite different -- a Multimap allows the association of multiple values to a single key. The result of executing the above code is as follows:
Answer=42
Hello=Mom
Hello=World
As can be seen, for the "Hello" key, the values of "Mom" and "World" associated with it. Unlike Hashtable, it does not discard one of the values and replace it with another. The Multimap is able to hold on to multiple values for each key.
Rather than give yet another multipmap answer, I'll ask why you want to do this?
Are the multiple values related? If yes, then it's probably better that you create a data structure to hold them. If no, then perhaps it's more appropriate to use separate maps.
Are you keeping them together so that you can iterate them based on the key? You might want to look for an alternative indexing data structure, like a SkipList.
Just make your own:
Map<Object, List<Object>> multiMap = new HashMap<Object, List<Object>>();
To add:
public void add(String key, Object o) {
List<Object> list;
if (multiMap.containsKey(key)) {
list = multiMap.get(key);
list.add(o);
} else {
list = new ArrayList<Object>();
list.add(o);
multiMap.put(key, list);
}
}
As others pointed out, no. Instead, consider using a Multimap which can map many values for the same key.
The Google Collections (update: Guava) library contains one implementation, and is probably your best bet.
Edit: of course you can do as Eric suggests, and store a Collection as a value in your Hashtable (or Map, more generally), but that means writing unnecessary boilerplate code yourself. When using a library like Google Collections, it would take care of the low-level "plumbing" for you. Check out this nice example of how your code would be simplified by using Multimap instead of vanilla Java Collections classes.
None of the answers indicated what I would do first off.
The biggest jump I ever made in my OO abilities was when I decided to ALWAYS make another class when it seemed like it might be even slightly useful--and this is one of the things I've learned from following that pattern.
Nearly all the time, I find there is a relationship between the objects I'm trying to place into a hash table. More often than not, there is room for a class--even a method or two.
In fact, I often find that I don't even want a HashMap type structure--a simple HashSet does fine.
The item you are storing as the primary key can become the identity of a new object--so you might create equals and hash methods that reference only that one object (eclipse can make your equals and hash methods for you easily). that way the new object will save, sort & retrieve exactly as your original one did, then use properties to store the rest of the items.
Most of the time when I do that, I find there are a few methods that go there as well and before I know it I have a full-fledged object that should have been there all along but I never recognized, and a bunch of garbage factors out of my code.
In order to make it more of a "Baby step", I often create the new class contained in my original class--sometimes I even contain the class within a method if it makes sense to scope it that way--then I move it around as it becomes more clear that it should be a first-class class.
See the Google Collections Library for multimaps and similar such collections. The built-in collections don't have direct support for this.
What you're looking for is a Multimap. The google collections api provides a nice implementation of this and much else that's worth learning to use. Highly recommended!
Simple. Instead of
Hashtable<Key, Value>, use Hashtable<Key, Vector<Value>>.
You need to use something called a MultiMap. This is not strictly a Map however, it's a different API. It's roughly the same as a Map<K, List<V>>, but you wont have methods like entrySet() or values().
Apart from the Google Collections there is a apache Commons Collection object
for MultiMap
Following code without Google's Guava library. It is used for double value as key and sorted order
Map<Double,List<Object>> multiMap = new TreeMap<Double,List<Object>>();
for( int i= 0;i<15;i++)
{
List<Object> myClassList = multiMap.get((double)i);
if(myClassList == null)
{
myClassList = new ArrayList<Object>();
multiMap.put((double) i,myClassList);
}
myClassList.add("Value "+ i);
}
List<Object> myClassList = multiMap.get((double)0);
if(myClassList == null)
{
myClassList = new ArrayList<Object>();
multiMap.put( (double) 0,myClassList);
}
myClassList.add("Value Duplicate");
for (Map.Entry entry : multiMap.entrySet())
{
System.out.println("Key = " + entry.getKey() + ", Value = " +entry.getValue());
}