Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way how to get the key (or the whole entry) from a `Map`?

Tags:

java

map

Is there any way how to get the key (or the whole entry) from a HashMap (or another suitable Map) efficiently?

Before somebody says, I never need it: I do. I have a loop like this

for (long i=0; i<1e12; ++i) {
    Key key = new Key(i);
    Value value = map.get(key);
    if (something(key, value)) list.add(key);
}

and my key needlessly takes memory which could be saved if I could replace list.add(key) by something like list.add(map.getKey(key)) (the new instance would be eligible for GC). While they're equal, reusing the old instance would save memory.

I know I can embed the key into the value or use Guava's Interner; both helps, but both also costs some memory.


To address some misunderstanding comments: If efficiency was no issue, the following would do

Key getKeyFromMap(Key key, Map<Key, ?> map) {
    for (Key key2 : map.keySet()) {
        if (key.equals(key2)) return key2;
    }
    return null;
}

The most efficient solution as described in the accepted answer:

public static <K, V> K getKey(K key, HashMap<K, V> map) {
    final Entry<K, V> entry = map.getEntry(key);
    return entry==null ? null : entry.getKey();
}

The problem is that it must be placed into package java.util as it uses a package private method. It can be dangerous to use such a method, but it's no problem in my "run-once" use case.

like image 979
maaartinus Avatar asked Oct 07 '12 14:10

maaartinus


1 Answers

How much evil are you prepared to commit in order to do this?

The Map interface does not let you retrieve a key or an entry. Nor does the Set interface. Nor does HashMap's public interface.

But HashMap's package interface does (in the Sun JDK, at least). Have a look at the source code; at line 355, there is a method called getEntry which starts like this:

  /**
   * Returns the entry associated with the specified key in the
   * HashMap.  Returns null if the HashMap contains no mapping
   * for the key.
   */
  final Entry<K,V> getEntry(Object key) {

I believe that is exactly what you need. You can call this with reflection, or by sneaking a class of your own into the java.util package. The Java maintainers could take this method away in the future, and it might not be present on all platforms, but if you're prepared to hold your nose and take the risk, it's a simple solution.

like image 141
Tom Anderson Avatar answered Oct 21 '22 14:10

Tom Anderson