Why Guava does not provide a way to transform map keys - java

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

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

Consistent and efficient bi-directional data structure implementation (Java)

I needed an implementation of a bi-directional map in Java so I tried to use BiMap and BidiMap from Guava and Commons. However, the inverse view capability is not maintained after a modification on an element. Here is an example with BiMap (same behavior with BidiMap) :
BiMap<Set<String>, Set<String>> map = HashBiMap.create();
Set<String> foo = new HashSet<>();
foo.add("foo");
Set<String> bar = new HashSet<>();
bar.add("bar");
map.put(foo, bar);
map.get(foo); // returns [bar], ok
map.inverse().get(map.get(foo)); // returns [foo], ok
map.get(foo).add("someString");
map.get(foo); // returns [bar, someString], ok
map.inverse().get(map.get(foo)); // returns null, not ok <=
Of course this behavior can be expected for an implementation using HashMaps but it illustrates the problem.
So the question is, is there a bi-directional data structure which can handle this kind of situation, with elements of arbitrary types, and still have a better average time complexity than an array of pairs?
EDIT : I'm not trying to solve this problem or avoid it, this is more of an academic question. I just want to know if such a data structure exists. That is, a data structure allowing bi-directional binding, mutable keys and with reasonable time complexity.
Your trouble is not with bidirectional maps, but with the assumption that you are allowed to modify a map key. Keys are in fact fundamentally required to be stable at least regarding the behavior of their equals and hashCode methods (in case of a hashtable-backed map) or their comparison method (in case of a binary tree-backed map).
Perhaps you can consider removing an element, changing it, then inserting it back—that's one way to meet the constraints of implementation.

Java or guava map implementation to use with multiple keys pointing to single value

I have a situation where many many keys are pointing to a single value. The situation arises from a service locator pattern that I am implementing such that -
each method in an interface is represented as a signature string
All such signatures of a single interface are used as keys
The value being the full canonical name of the implementation class
Thus my need is to retrieve a single value when user requests any of the matching keys.
In a sense I need an opposite of MultiMap from Guava .
I am looking for the most optimized solution there is since my keys are very similar though unique for a specific value and I am not sure if using a generic Map implementation like HashMap is efficient enough to handle this case.
e.g. all the below signatures
==============
_org.appops.server.core.service.mocks.MockTestService_testOperationThree
_org.appops.server.core.service.mocks.MockTestService_getService
_org.appops.server.core.service.mocks.MockTestService_start
_org.appops.server.core.service.mocks.MockTestService_testOperationTwo_String_int
_org.appops.server.core.service.mocks.MockTestService_getName
_org.appops.server.core.service.mocks.MockTestService_shutdown
_org.appops.server.core.service.mocks.MockTestService_testOperationOne_String
=======
Point to a single class i.e. org.appops.server.core.service.mocks.MockTestServiceImpl and I am anticipating hundreds of such classes (values) and thousands of such similar signatures (keys) .
In case there is no optimized way I could always use a HashMap with replicated values for each group of keys which I would like to avoid.
Ideally I would like to use a ready utility from Guava.
HashMap is actually what you need, and the issue is that you misunderstand what it does.
In case there is no optimized way I could always use a HashMap with replicated values for each group of keys which I would like to avoid.
HashMap does not store a copy of the value for each key mapping to that value. HashMap stores a reference to the Java object. It's always the same cost. A HashMap<Integer, BigExpensiveObject> where every key is mapped to the same BigExpensiveObject takes exactly the same amount of memory as a HashMap<Integer, Integer> where every key is mapped to the same Integer. The only memory difference in the whole program would be the memory difference between one BigExpensiveObject and one Integer.

Case insensitive HashMap which retains the original key?

I need to store keys as case insensitive, and all values for keys like STATE/state/State are merged into one Set. However the catch is I need the case sensitive version of the original key back at some point so a generic CaseInsensitiveMap doesn't work. I only need back the first capitalization of 'state' added, so in this case I keep STATE and discard state/State.
I've looked at a few options for implementing this data structure, like Guava HashMultimap and Tuples, but none seem quite right.
<CaseInsensitiveOriginalKey, OriginalKey, Set<Values>>
So for example if I add a key 'State' with values {Texas, Oklahoma} it will be stored as:
<state, State, {Texas, Oklahoma}>
The idea being if I create some kind of .add(StATe, {Nebraska}) then the map, seeing a case-insensitive entry for 'state' already exists, becomes:
<state, State, {Texas, Oklahoma, Nebraska}>
and for a new key, .add(COLOR, {blue, red})
The overall map becomes:
<state, State, {Texas, Oklahoma, Nebraska}>
<color, COLOR, {blue, red}>
.get(ColoR) returns {red, blue}
.getKey(coLOR) returns COLOR
Any ideas on how to best accomplish this?
You can maintain two maps:
One is a Map<String, Set<String>> that maps the case-insensitive key to the corresponding set of strings (e.g. "state" → {"Texas", "Oklahoma"}).
The other is a Map<String, String> that maps the case-insensitive key to its corresponding case-sensitive key (e.g. "state" → "State").
You can create your own class that has these two maps as private fields and ensures that they are kept in sync whenever a pairing is added/removed/updated.
What you need is something like Map<CaseInsensitiveOriginalKey, Record> where Record is a custom class with the original (case-sensitive) key and the set of values as attributes.
You could get away with using a generic Pair class instead of a custom Record class, but (IMO) that would be poor design.
However, there is a problem with your requirements:
However the catch is I need the case sensitive version of the original key back ...
Your examples indicate that you could have multiple case sensitive versions of the original key; i.e. the one that you saw first (e.g. "State") and subsequent ones (e.g. "STate", "state", etc). So which is the correct original key to use? And what about the case where the first one you saw was ... erm ... junky?
The point is that treating the first version that you saw as the definitive / preferred one is going to be problematic. You need something (or someone) to figure out the definitive version intelligently. To do that you probably need to keep all of the versions that you saw until (at least) you completed the initial data capture phase. You may even need to keep their frequencies and/or their contexts.
I'd suggest a data structure that has a couple of maps. One is a map from each (case-sensitive) key to the case-insensitive key and the other is a map from the case-insensitive key to the value. Given a case-sensitive key, each access would be a two-step affair: find the case-insensitive key to use from the first map and then use the key with the second map.

Map with two-dimensional key in java

I want a map indexed by two keys (a map in which you put AND retrieve values using two keys) in Java. Just to be clear, I'm looking for the following behavior:
map.put(key1, key2, value);
map.get(key1, key2); // returns value
map.get(key2, key1); // returns null
map.get(key1, key1); // returns null
What's the best way to to it? More specifically, should I use:
Map<K1,Map<K2,V>>
Map<Pair<K1,K2>, V>
Other?
(where K1,K2,V are the types of first key, second key and value respectively)
You should use Map<Pair<K1,K2>, V>
It will only contain one map,
instead of N+1 maps
Key construction
will be obvious (creation of the
Pair)
Nobody will get confused as to
the meaning of the Map as its
programmer facing API won't have changed.
Dwell time in the data structure would be shorter, which is good if you find you need to synchronize it later.
If you're willing to bring in a new library (which I recommend), take a look at Table in Guava. This essentially does exactly what you're looking for, also possibly adding some functionality where you may want all of the entries that match one of your two keys.
interface Table<R,C,V>
A collection that associates an
ordered pair of keys, called a row key
and a column key, with a single value.
A table may be sparse, with only a
small fraction of row key / column key
pairs possessing a corresponding
value.
I'd recommend going for the second option
Map<Pair<K1,K2>,V>
The first one will generate more overload when retrieving data, and even more when inserting/removing data from the Map. Every time that you put a new Value V, you'll need to check if the Map for K1 exists, if not create it and put it inside the main Map, and then put the value with K2.
If you want to have an interface as you're exposing initially wrap your Map<Pair<K1,K2>,V> with your own "DoubleKeyMap".
(And don't forget to properly implement the methods hash and equals in the Pair class!!)
While I also am on board with what you proposed (a pair of values to use as the key), you could also consider making a wrapper which can hold/match both keys. This might get somewhat confusing since you would need to override the equals and hashCode methods and make that work, but it could be a straightforward way of indicating to the next person using your code that the key must be of a special type.
Searching a little bit, I found this post which may be of use to you. In particular, out of the Apache Commons Collection, MultiKeyMap. I've never used this before, but it looks like a decent solution and may be worth exploring.
I would opt for the Map<Pair<K1,K2>, V> solution, because:
it directly expresses what you want to do
is potentially faster because it uses fewer indirections
simplifies the client code (the code that uses the Map afterwards
Logically, you Pair (key1, key2) corresponds to something since it is the key of your map. Therefore you may consider writing your own class having K1 and K2 as parameters and overriding the hashCode() method (plus maybe other methods for more convenience).
This clearly appears to be a "clean" way to solve your problem.
I have used array for the key: like this
Map<Array[K1,K2], V>

Categories