I have one Map in java like this:
Map<String index1, Map<String index 2, Object obj>> map = new HashMap<>();
I want to get my Object
in the map by using index1
and index2
as lookups.
I have one Map in java like this:
Map<String index1, Map<String index 2, Object obj>> map = new HashMap<>();
I want to get my Object
in the map by using index1
and index2
as lookups.
The easiest way to do this would be to use Guava's Table
, if you're willing to use a third party library.
It works like this:
Table<String, String, Object> table = HashBasedTable.create();
table.put(index1, index2, obj);
Object retrievedObject = table.get(index1, index2);
You can add it to your project by following these instructions: How to add Guava to Eclipse project
If you don't want to use Guava, you have a big problem. If you try to insert an element with new first key, you have to make sure the innermap already exists. This means, every time you do put
, you have to retrieve the innerMap
, see if it exists, and then create it if it does not. You will have to do this every time you call Map.put
. Also, you risk throwing a NullPointerException
if the inner map doesn't exist when you call get
on the inner map.
If you do this, should wrap your Map<String, Map<String, Object>
in an outer class to manage these problems, or use Java 8's computeIfAbsent
. But the easiest way is to just use Table
as above.
If you make your own class to use instead of Table
, it would be something like:
public class DoubleMap<R, C, V> {
private final Map<R, Map<C, V>> backingMap;
public DoubleMap() {
this.backingMap = new HashMap<>();
}
public V get(R row, C column) {
Map<C, V> innerMap = backingMap.get(row);
if(map == null) return null;
else return innerMap.get(column);
}
public void put(R row, C column, V value) {
Map<C, V> innerMap = backingMap.get(row);
if(innerMap == null) {
innerMap = new HashMap<C, V>();
backingMap.put(row, innerMap);
}
innerMap.put(column, value);
}
}
You would use this class by doing:
DoubleMap<String, String, Object> map = new DoubleMap();
Note that this answer has a lot less features than the Guava version.
Map
If I understand your question, then with an index a
and b
that might look like (guarding against null
with a ternary or Conditional Operator ? :
),
Object obj = (map.get("a") == null) ? null : map.get("a").get("b");
And you might be more specific, like
Map<String, Map<String, Something>> map = new HashMap<>();
Something s = (map.get("a") == null) ? null : map.get("a").get("b");
Map
Assuming you want to add your Something value
to the map
that could be done with something like,
Map<String, Map<String, Something>> map = new HashMap<>();
if (map.get("a") == null) {
map.put("a", new HashMap<>());
}
map.get("a").put("b", value);
If you don't need regular access to the entire "row", but just quick access to each cell you can use the built-in Map.Entry
as your key:
Map<Map.Entry<String, String>, Object> table = new Map<>();
table.put(new Map.SimpleEntry("index1", "index2"), "Hello world");
Alternatively, if you're willing to go with something third-party, several someones have already implemented tuples for Java.
If you are in a situation where you cannot pull in a third-party library easily, but you don't like the semantics of Map.Entry
(which is written in terms of key
s and value
s) you can write your own Pair
class to have the same effect.
As my understanding, you can do like:
Map<String, Map<String, Object> map= new HashMap();
Map<String, Object> subMap = map.get("index1");
if(subMap != null) {
Object obj = subMap.get("index2");
}
The best solution probably depends on how this map is intended to be used:
String
, or do they have to be generic?A pragmatic solution focussed on the question as you described it would be to introduce a StringPair
class that can be used for indexing. This saves you from the hassle of doing 2D-lookups of inner maps (and possible cleanups when the inner maps become empty!), does not require any third-party libraries, and is readable and efficient.
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
public class StringPairMapTest
{
public static void main(String[] args)
{
Map<StringPair, Object> map = new LinkedHashMap<StringPair, Object>();
map.put(StringPair.of("A","B"), 12);
map.put(StringPair.of("C","D"), 34);
System.out.println(map.get(StringPair.of("A","B")));
System.out.println(map.get(StringPair.of("C","D")));
System.out.println(map.get(StringPair.of("X","Y")));
}
}
class StringPair
{
private final String s0;
private final String s1;
static StringPair of(String s0, String s1)
{
return new StringPair(s0, s1);
}
private StringPair(String s0, String s1)
{
this.s0 = s0;
this.s1 = s1;
}
@Override
public String toString()
{
return "("+s0+","+s1+")";
}
@Override
public int hashCode()
{
return Objects.hash(s0, s1);
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StringPair other = (StringPair) obj;
return Objects.equals(s0, other.s0) && Objects.equals(s1, other.s1);
}
}
Generalizations to a Pair<T>
or Tuple<S,T>
would be possible, of course, but this did not seem to be what you have been looking for...