简体   繁体   中英

Efficient way invert to invert a hashmap with a small number of the keys mapping to same values

I have a hashmap that that I know that some keys map to the same values.
The number of these keys is very small (less than 6%) and they map between 2-4 values.
Eg

Map<String, String> map = new HashMap<>();  
map.put("codeA", "100");  
map.put("codeB", "7");  
map.put("codeC", "0012");   

I need to create an inverse of this map from the values to the keys so I did:

inverseMap = new HashMap<String, ArrayList<String>>();
for(Map.Entry<String, String> e:map.entrySet()) {
    String code = e.getKey();
    String val = e.getValue();
    ArrayList<String> codesColliding = inverseMap.get(val);
    if(codesColliding == null) {
        codesColliding = new ArrayList<>(4);
        inverseMap.put(val, codesColliding);
    }
    codesColliding.add(code);
}  

This works but I think it is suboptimal as I am using more memory than needed for the vast majority of the keys.
Although from coding perspective it works I was wondering if this can be approached differently (via other data structures?)
Note: I am interested in plain Java 7 (no extra libs) approaches

If the values of the inverse map need to be able to accommodate multiple keys from the original map, then there is no avoiding some overhead relative to the case when they do not need to be so accommodating. Your current approach isn't bad, but if so small a percentage of the original map's values are duplicated, and none are duplicated more than a handful of times, then I'd be even more stingy with the initial capacities of the lists you use as values in the inverse map. Why pre-allocate any more than one element? You'll rarely need to re-allocate, but when you do, the list will handle it transparently to you.

Maybe the easiest approach is to create a class that has two HashMaps, one for non colliding keys, the other for keys that collide. If you disambiguate the collisions in a certain way (eg, you always pick the first one alphabetically) you can add that logic into the class. Or you can lazily wrap non colliding Strings into an ArrayList, if you want to return ArrayLists.

It's all about knowing what you want to do with the Map. You can even sacrifice some type safety if you are confident your code can handle disambiguating between String and ArrayList results.

I know you're talking about a Map<String,String> , but just for clarity let's generalize it to Map<K,V> , from which you're building a Map<V,Collection<K>> . Add another Map<V,K> , maybe call it uniqueInverseMap . As you scan through the entries, always check for a key first in inverseMap , then uniqueInverseMap . If it's already in uniqueInverseMap , remove it, create a new two-element list, add the list to inverseMap .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM