'Java - how to get a key object (or entry) stored in HashMap by key?
I'd like to get the "canonical" key object for each key usable to query a map. See here:
Map<UUID, String> map = new HashMap();
UUID a = new UUID("ABC...");
map.put(a, "Tu nejde o zamykání.");
UUID b = new UUID("ABC...");
String string = map.get(b); // This gives that string.
// This is what I am looking for:
UUID againA = map.getEntry(b).key();
boolean thisIsTrue = a == againA;
A HashMap uses equals(), which is the same for multiple unique objects. So I want to get the actual key from the map, which will always be the same, no matter what object was used to query the map.
Is there a way to get the actual key object from the map? I don't see anything in the interface, but perhaps some clever trick I overlooked?
(Iterating all entries or keys doesn't count.)
Solution 1:[1]
Is there a way to get the actual key object from the map?
OK, so I am going to make some assumptions about what you mean. After all, you said that your question doesn't need clarification, so the obvious meaning that I can see must be the correct one. Right? :-)
The answer is No. There isn't a way.
Example scenario (not compileable!)
UUID uuid = UUID.fromString("xxxx-yyy-zzz");
UUID uuid2 = UUID.fromString("xxxx-yyy-zzz"); // same string
println(uuid == uuid2); // prints false
println(uuid.equals(true)); // prints true
Map<UUID, String> map = new ...
map.put(uuid, "fred");
println(map.get(uuid)); // prints fred
println(map.get(uuid2)); // prints fred (because uuid.equals(uuid2) is true)
... but, the Map API does not provide a way to find the actual key (in the example above it is uuid) in the map apart from iterating the key or entry sets. And I'm not aware of any existing Map class (standard or 3rd-party) that does provide this1.
However, you could implement your own Map class with an additional method for returning the actual key object. There is no technical reason why you couldn't, though you would have more code to write, test, maintain, etcetera.
But I would add that I agree with Jim Garrison. If you have a scenario where you have UUID objects (with equality-by-value semantics) and you also want to implement equality by identity semantics, then there is probably something wrong with your application's design. The correct approach would be to change the UUID.fromString(...) implementation to always return the same UUID object for the same input string.
1 - This is not to say that such a map implementation doesn't exist. But if it does, you should be able to find it if you look hard enough Note that Questions asking us to find or recommend a library are off-topic!
Solution 2:[2]
There is a (relatively) simple way of doing this. I’ve done so in my applications from time to time, when needed ... not for the purpose of == testing, but to reduce the number of identical objects being stored when tens of thousand of objects exist, and are cross-referenced with each other. This significantly reduced my memory usage, and improved performance ... while still using equals() for equality tests.
Just maintain a parallel map for interning the keys.
Map<UUID, UUID> interned_keys = ...
UUID key = ...
if (interned_keys.contains(key))
key = interned_keys.get(key)
Of course, it is far better when the object being stored knows what its own identity is. Then you get the interning basically for free.
class Item {
UUID key;
// ...
}
Map<UUID, Item> map = ...
map.put(item.key, item);
UUID key = ...
key = map.get(key).key; // get interned key
Solution 3:[3]
I think there are valid reasons for wanting the actual key. For example, to save memory. Also keep in mind that the actual key may store other objects. For instance, suppose you have a vertex of a graph. The vertex can store the actual data (Say a String, for instance), as well as the incident vertices. A vertex hash value can be dependent only on the data. So to look up a vertex with some data, D, look up a vertex with data, D,and with with no incident values. Now if you can return the actual vertex in the map you will be able to get the actual incident to the vertex.
It seems to me that many map implementations could easily provide a getEntry method. For example, the HashMap implementation for get is:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
One could use the getNode method to return an Entry:
public getEntry(Object key){
Node<K,V> e = getNode(hash(key),key);
if(e == null) return null;
return new Entry<>(e.key,e.value);
}
Solution 4:[4]
The easiest way is to duplicate the reference to the key in the value using a generic Pair type, like this:
HashMap<UUID,Pair<UUID,String>> myMap = new HashMap<>();
When you put them in the map, you provide the reference to the key to the pair. The cost is one reference per entry.
void add(UUID uuid, String str)
{
myMap.put(uuid,Pair.of(uuid,str));
}
Pair<UUID,String> get(UUID uuid)
{
return myMap.get(uuid);
}
Then getFirst() of the Pair is your key. getSecond() is the value.
Whatever you do, it's going to cost you in either time or space.
Your Pair class will be something like:
public class Pair<A,B>
{
private final A a;
private final B b;
public Pair(A a, B b)
{
this.a = a;
this.b = b;
}
/**
* @return the first argument of the Pair
*/
public A getFirst()
{
return this.a;
}
/**
* @return the second argument of the Pair
*/
public B getSecond()
{
return this.b;
}
/**
* Create a Pair.
*
* @param a The first argument (of type A)
* @param b The second argument (of type B)
*
* @return A Pair of A and B
*/
public static <A,B> Pair<A,B> of(A a, B b)
{
return new Pair<>(a,b);
}
// Don't forget to get your IDE to produce a hashcode()
// and equals() method for you, depending
// on if you allow nulls or not, or DIY.
}
Solution 5:[5]
it could help. You can use a for each like below.
Map<String,Object> map = new HashMap<>();
map.put("hello1", new String("Hello"));
map.put("hello2", new String("World"));
map.put("hello3", new String("How"));
map.put("hello4", new String("Are u"));
for(Map.Entry<String,Object> e: map.entrySet()){
System.out.println(e.getKey());
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | AJNeufeld |
| Solution 3 | dan b |
| Solution 4 | bluejazzshark |
| Solution 5 | Jorge L. Morla |
