Map with two-dimensional key in java - 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>

Related

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.

Why is Set at all required when we have Maps?

Sets are essentially Maps from an existential point of view. There is nothing a Map can not do which a Set can, I assume. We have these overheads of defining key-value pairs in Maps which is not there in the Sets. But again the elements of a Set are just the keys of the underlying Map, right? So what is the point of having Sets around when Maps are able to do all the things required? I hope a Set takes the same amount of memory as a Map does?
What are key arguments in favor of existence of Sets?
For instance, in the case of Lists, we have ArrayList and LinkedList which have differences and we can choose between these two as per our requirements.
I would argue that a Map is actually a Set!
Map<Key,Value> can be implemented with Set<Entry<Key,Value>>
This is similar to the mathematical foundations of what sets, maps, and functions are.
Firstly, can we agree that a Map is a function from Key=>Value (or Domain=>Range). Each key corresponds with at most one value, so it is a partial function (or a complete function only upon those keys in the map). So a map is a function. (Scala even goes so far as to have Map implement the Function1 interface.)
Secondly, what is a function? A function is a set of tuples where each first element occurs only once in the set. The second element of the tuple is the value returned by the function.
So we have Map is a Function is a Set.
On a practical note, there are very good reasons for having Sets. They are very often the correct data structure to use from a conceptual point of view, even before you start worrying about performance. I'd use them over a List in most situations.
The primary difference between a Set and a Map is that a Map holds two object per Entry e.g. key and value and it may contain duplicate values but keys are always unique. But Set holds only keys and those are unique.

Comparing keys of a multi-dimensional HashMap

I am working on an application with a number of custom data classes. I am taking input for my application from 2 different places and want to cross-reference between the two to help ensure the data is accurate.
I have a Map.Entry<String,HashMap<String, Integer>> object called chromosome, where each value is called a marker.
I also have a custom object called IndividualList individuals which extends HashMap<Integer,Individual> where each Individual has a method Genotype getGenotype() which returns the non-static variable genotype. Genotype extends HashMap<String,String[]>
I want to look at each the key for all my marker objects and check whether each of them are present as a key in any Individual's genotype. Every Individual has the same keys in its genotype so I only need to test for one Individual.
The problem I am facing is which Individual to test, as because it is a HashMap I cannot simply just arbitrarily choose the first element, so what I am doing at the moment is taking the values of individuals as a Collection then converting these to an ArrayList<Individual> then taking the first of these elements (which is just an arbitrary one as HashMap is unordered) to get an Individual then taking this Individual's genotype and comparing marker.getKey() with the keys in the genotype. Like so :
for(Map.Entry<String, MarkerPosition> marker : chromosome.getValue().entrySet())
if(!(new ArrayList<Individual>(individuals.values()).get(0)
.getGenotype().containsKey(marker.getKey())))
errors.add("Marker " + marker.getKey() + " is not present in genotype");
But as you can see, this is horrid and ugly and far too complicated, so I was wondering if there is a much simpler way of achieving what I want that I am missing.
Thanks!
Why can you not arbitrarily choose the first element of a HashMap?
individuals.entrySet().iterator().next()
individuals.values().iterator().next()
This will probably be the same entry each time. You should make sure the map is not empty to avoid an exception.
...This question is really confusingly phrased and difficult to understand, but I'm not clear on why you don't just use
individuals.values().iterator().next()
instead of new ArrayList<Individual>(individuals.values()).get(0).
(If you can use third-party libraries, your code would probably be significantly clearer overall if you used a Guava Table, which is a general-purpose, significantly "cleaner" replacement for a Map<K1, Map<K2, V>>. Disclosure: I contribute to Guava.)

can a Map also be a Collection?

I'd like to have a Map that is also a Collection. Or more specifically, I'd like to be able to iterate over the entries in a Map, including the case where there are multiple entries for a particular key.
The specific problem I'm trying to solve is providing an object that can be used in jstl both to iterate over using c:forEach and in an expression like ${a.b.c}. In this example, I'd want ${a.b.c} to evaluate to the the first value of c (or null if there are none), but also be able to iterate over all cs with <c:forEach items="${a.b.c}"> and have the loop body see each individual value of c in turn, although they have the same key in the Map.
Looking at things from a method point of view, this should be straightforward, just provide a Map implementation whose entrySet() method returns a set with multiple Entries with the same key. But since this seems to violate the contract of a Map, will things break in subtle yet disastrous ways? Has anyone else done this sort of thing?
(If you guessed I'm trying to present xml, you'd be correct)
EDIT
Please note that this is for use in jstl, so whatever interface I present must meet 2 conditions:
for use with the [] and . operators, it must be a Map, List, array or JavaBeans object (and of those it can't be a List or array because the indexes will not be numbers)
for use with forEach it must be an array, Collection, Iterator, Enumeration, Map, or String.
So I guess the real question is, can I count on jstl only calling .containsKey(), .get(), and .entrySet() and not caring about invariants being violated, and not internally making a copy of the Map which would not preserve the special iteration behavior.
What you are looking for is a Multimap. Guava provides an implementation of it and specifically you are looking for ArrayListMultimap.
I barely remember jstl, but what you're saying sounds a kind of controversial:
In foreach:
here ${a.b.c} should point to some container of values and then we iterate over it.
On the other hand you say, ${a.b.c} "should evaluate to the the first value of c" (or null...)
Its an ambiguous definition.
If you feel like Multimap is not what you want, you can provide your own collection implementation (probably internally based on Multimap)
Just as an idea you can always look at a single element as a list (that accidentally
is comprised of one element). This way you would resolve your ambiguity, I guess.
I hope this helps
Having a Map with multiple entries for the same key irreparably breaks the Map contract. If Multimap doesn't work for you, then there's no way to do this without breaking a lot of things.
Specifically, if you pass your monstrosity to something that's specified to take a Map, it'll almost certainly break...and it sounds like that's what you want to do with it, so yeah.
how about you use a Map with Collections as values? then you can have different values for the same key and you can iterate over them by a nested foreach-loop
you can also easily write a wrapper for an existing map-implementation, which gives you a single iterator over all values, if you need it that way

Searching an object in a java map

I am new to Java, I was working with Map class and its derivatives.
I was just wondering about how elements are found inside them. Is only a pointer/reference check performed?
Let's say I have a TreeMap<MyObject, Integer>. If I have an object x i would like you to search an integer v such that its key is "equal" to x even if they are 2 separate instances of the class MyObject, hence 2 different pointers.
Is there any method (of an interface/superclass too) which can it do such operation?
Thanks in advance.
All the methods that involve comparisons in Map and its implementations make use of the 'equals' method for the objects. If you attempt to add a key+value to a Map which already contains aentry with a key that would compare equals to it, then the new key+value replaces the old one.
See the documentation:
For example, the specification for the containsKey(Object key) method says: "returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k))."
The implementation may not execute any equals comparison if it can determine that the keys are 'unequal' through some other means, such as comparing hashcodes.
In your example, you would do
TreeMap<MyObject, Integer> tree = ...
Integer i = tree.get(x);
The get(x) will iterate over your keys() and returning the integer value for the key matching aKey.equals(x).
In most cases, Maps are backed by a hash table, and are very similar to a HashMap. TreeMap gives a bit more information with each node by having pointers up and down the tree, but lookups are still done via hashes (I believe)

Categories