Java map key depending on value - java

Is it a good practice to have a map key depending on value?
e.g.:
class MyClass {
private String key;
private Object value;
}
And then:
Map<String, MyClass> map = new LinkedHashMap<String, MyClass>();
MyClass a = new MyClass("key1", valueObject);
map.put(a.getKey(), a);
Is it ok? I am forced to have such a class as value, I thought about using Set but I need to get item based on index (geting by keySet index) and key (where values key == maps key). I also need fixed size with possibility of removing oldest element from this collection.
I think i should ensure that my key and values key field will be always the same. How can i achieve it?

Having the keys in the value entities is totally fine, this is how every database creates indices. A good practice would be to have MyClass to be immutable and wrap the whole thing in a new collection class hiding details and preventing inserting values to the wrong keys. This is the way to ensure that key == value.key

Your map store redundant data(double key).
You can put MyClass.key as map's key and MyClass.value as map's value into the map.

As you can see here a Map maps a key to its value.
So your attempt is a bit redundant, as long as you don't need the key for further operations inside MyClass.

Related

Java Map get key from value

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

How to retrieve a key based on the value in Map Interface Java?

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.

Does a HashMap use a HashSet to store its keys?

I'm wondering if a HashMap uses a HashSet to store its keys. I would guess it does, because a HashMap would correspond with a HashSet, while a TreeMap would correspond with a TreeSet.
I looked at the source code for the HashMap class, and the method returns an AbstractSet that's implemented by some kind of Iterator.
Additionally...when I write
HashMap map = new HashMap();
if(map.keySet() instanceof HashSet){
System.out.println("true");
}
The above if statement never runs. Now I'm unsure
Could someone explain how the HashMap stores its keys?
You're actually asking two different questions:
Does a HashMap use a HashSet to store its keys?
Does HashMap.keySet() return a HashSet?
The answer to both questions is no, and for the same reason, but there's no technical reason preventing either 1. or 2. from being true.
A HashSet is actually a wrapper around a HashMap; HashSet has the following member variable:
private transient HashMap<E,Object> map;
It populates a PRESENT sentinel value as the value of the map when an object is added to the set.
Now a HashMap stores it's data in an array of Entry objects holding the Key, Value pairs:
transient Entry<K,V>[] table;
And it's keySet() method returns an instance of the inner class KeySet:
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> {
// minimal Set implementation to access the keys of the map
}
Since KeySet is a private inner class, as far as you should be concerned it is simply an arbitrary Set implementation.
Like I said, there's no reason this has to be the case. You could absolutely implement a Map class that used a HashSet internally, and then have your Map return a HashSet from .keySet(). However this would be inefficient and difficult to code; the existing implementation is both more robust and more efficient than naive Map/Set implementations.
Code snippets taken from Oracle JDK 1.7.0_17. You can view the source of your version of Java inside the src.zip file in your Java install directory.
I'm wondering if a HashMap uses a HashSet to store its keys.
That would not work too well, because a Set only keeps track of the keys. It has no way to store the associated value mapping.
The opposite (using a Map to store Set elements) is possible, though, and this approach is being used:
HashSet is implemented by using a HashMap (with a dummy value for all keys).
The set of keys returned by HashMap#keySet is implemented by a private inner class (HashMap.KeySet extends AbstractSet).
You can study the source for both class, for example on GrepCode: HashMap and HashSet.
Could someone explain how the HashMap stores its keys?
It uses an array of buckets. Each bucket has a linked list of entries. See also
How does Java HashMap store entries internally
Hashmap and how this works behind the scene
The set that is returned by the keySet is backed by the underlying map only.
As per javadoc
Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.
Blockquote
HashMap stores keys into buckets. Keys that have same hash code goes into the same bucket. When retrieving value for an key if more than one key is found in the bucket than equals method is used to find the right key and hence the right value.
Answer is: NO.
HashMap.keySet() is a VIEW of the keys contained in this map.
The data of the map is stored in Entry[] table of HashMap.

Java: Setting key of a hashmap explicitly and keeping reference to it

public static HashMap<ArrayList<Integer>, String> map = new HashMap<ArrayList<Integer>, String>();
public static ArrayList<ArrayList<Integer>> keys = new ArrayList<>(map.keySet());
Then in main
map.put(key, "c");
(assume key is a valid ArrayList). But keys still has size 0 after that.
How can I make the relationship of keys stronger so that it will be actually tied to the HashMap and contain all its keys.
The copy constructor of ArrayList copies all the keys in the map to the ArrayList but if you change the map after that point it will not be reflected.
I can think of 3 options:
write your own map implementation that embeds an ArrayList and keeps it up to date
update the ArrayList manually everytime you update the map
don't use an ArrayList at all (keySet() is there when you need to access the keys so I'm not sure why you would need one)
You can't.
Map.keySet() returns the Map's current key set, which you then load into your list. Changes to the map after that have no effect on the contents of the list.
Most people would just re-get the key set if needed. Why don't you just do that?

How does Java HashMap store entries internally

Say you have a key class (KeyClass) with overridden equals, hashCode and clone methods. Assume that it has 2 primitive fields, a String (name) and an int (id).
Now you define
KeyClass keyOriginal, keyCopy, keyClone;
keyOriginal = new KeyClass("original", 1);
keyCopy = new KeyClass("original", 1);
keyClone = KeyClass.clone();
Now
keyOriginal.hashCode() == keyCopy.hashCode() == keyClone.hashCode()
keyOriginal.equals(keyCopy) == true
keyCopy.equals(keyClone) == true
So as far as a HashMap is concerned, keyOriginal, keyCopy and keyClone are indistinguishable.
Now if you put an entry into the HashMap using keyOriginal, you can retrieve it back using keyCopy or keyClone, ie
map.put(keyOriginal, valueOriginal);
map.get(keyCopy) will return valueOriginal
map.get(keyClone) will return valueOriginal
Additionally, if you mutate the key after you have put it into the map, you cannot retrieve the original value. So for eg
keyOriginal.name = "mutated";
keyOriginal.id = 1000;
Now map.get(keyOriginal) will return null
So my question is
when you say map.keySet(), it will return back all the keys in the map. How does the HashMap class know what are the complete list of keys, values and entries stored in the map?
EDIT
So as I understand it, I think it works by making the Entry key as a final variable.
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
(docjar.com/html/api/java/util/HashMap.java.html). So even if I mutate the key after putting it into the map, the original key is retained. Is my understanding correct? But even if the original key reference is retained, one can still mutate its contents. So if the contents are mutated, and the K,V is still stored in the original location, how does retrieval work?
EDIT
retrieval will fail if you mutate the key after putting into the hashmap. Hence it is not recommended that you have mutable hashmap keys.
HashMap maintains a table of entries, with references to the associated keys and values, organized according to their hash code. If you mutate a key, then the hash code will change, but the entry in HashMap is still placed in the hash table according to the original hash code. That's why map.get(keyOriginal) will return null.
map.keySet() just iterates over the hash table, returning the key of each entry it has.
If you change the entry but not the hashCode, you are safe. For this reason it is considered best practice to make all fields in the hashCode, equals and compareTo, both final and immutable.
Simply put, the HashMap is an object in your computer's memory that contains keys and values. Each key is unique (read about hashcode), and each key points to a single value.
In your code example, the value coming out of your map in each case is the same because the key is the same. When you changed your key, there is no way to get a value for it because you never added an item to your HashMap with the mutated key.
If you added the line:
map.put("mutated", 2);
Before mutating the key, then you will no longer get a null value.

Categories