[java] How do I access nested HashMaps in Java?

I have a HashMap in Java, the contents of which (as you all probably know) can be accessed by

HashMap.get("keyname");

If a have a HashMap inside another HashMap i.e. a nested HashMap, how would i access the contents? Can i do this like this, inline:

HashMap.get("keyname").get("nestedkeyname");

Thank you.

This question is related to java hashmap

The answer is


I came to this StackOverflow page looking for a something ala valueForKeyPath known from objc. I also came by another post - "Key-Value Coding" for Java, but ended up writing my own.

I'm still looking for at better solution than PropertyUtils.getProperty in apache's beanutils library.

Usage

Map<String, Object> json = ...
public String getOptionalFirstName() {
    return MyCode.getString(json, "contact", "firstName");
}

Implementation

public static String getString(Object object, String key0, String key1) {
    if (key0 == null) {
        return null;
    }
    if (key1 == null) {
        return null;
    }
    if (object instanceof Map == false) {
        return null;
    }
    @SuppressWarnings("unchecked")
    Map<Object, Object> map = (Map<Object, Object>)object;
    Object object1 = map.get(key0);
    if (object1 instanceof Map == false) {
        return null;
    }
    @SuppressWarnings("unchecked")
    Map<Object, Object> map1 = (Map<Object, Object>)object1;
    Object valueObject = map1.get(key1);
    if (valueObject instanceof String == false) {
        return null;
    }
    return (String)valueObject;
}

I hit this discussion while trying to figure out how to get a value from a nested map of unknown depth and it helped me come up with the following solution to my problem. It is overkill for the original question but maybe it will be helpful to someone that finds themselves in a situation where you have less knowledge about the map being searched.

private static Object pullNestedVal(
        Map<Object, Object> vmap,
        Object ... keys) {
    if ((keys.length == 0) || (vmap.size() == 0)) {
        return null;
    } else if (keys.length == 1) {
        return vmap.get(keys[0]);
    }

    Object stageObj = vmap.get(keys[0]);
    if (stageObj instanceof Map) {
        Map<Object, Object> smap = (Map<Object, Object>) stageObj;
        Object[] skeys = Arrays.copyOfRange(keys, 1, keys.length);
        return pullNestedVal(smap, skeys);
    } else {
        return null;
    }
}

If you plan on constructing HashMaps with variable depth, use a recursive data structure.

Below is an implementation providing a sample interface:

class NestedMap<K, V> {

    private final HashMap<K, NestedMap> child;
    private V value;

    public NestedMap() {
        child = new HashMap<>();
        value = null;
    }

    public boolean hasChild(K k) {
        return this.child.containsKey(k);
    }

    public NestedMap<K, V> getChild(K k) {
        return this.child.get(k);
    }

    public void makeChild(K k) {
        this.child.put(k, new NestedMap());
    }

    public V getValue() {
        return value;
    }

    public void setValue(V v) {
        value = v;
    }
}

and example usage:

class NestedMapIllustration {
    public static void main(String[] args) {

        NestedMap<Character, String> m = new NestedMap<>();

        m.makeChild('f');
        m.getChild('f').makeChild('o');
        m.getChild('f').getChild('o').makeChild('o');
        m.getChild('f').getChild('o').getChild('o').setValue("bar");

        System.out.println(
            "nested element at 'f' -> 'o' -> 'o' is " +
            m.getChild('f').getChild('o').getChild('o').getValue());
    }
}

I prefer creating a custom map that extends HashMap. Then just override get() to add extra logic so that if the map doesnt contain your key. It will a create a new instance of the nested map, add it, then return it.

public class KMap<K, V> extends HashMap<K, V> {

    public KMap() {
        super();
    }

    @Override
    public V get(Object key) {
        if (this.containsKey(key)) {
            return super.get(key);
        } else {
            Map<K, V> value = new KMap<K, V>();
            super.put((K)key, (V)value);
            return (V)value;
        }
    }
}

Now you can use it like so:

Map<Integer, Map<Integer, Map<String, Object>>> nestedMap = new KMap<Integer, Map<Integer, Map<String, Object>>>();

Map<String, Object> map = (Map<String, Object>) nestedMap.get(1).get(2);
Object obj= new Object();
map.put(someKey, obj);

Yes, if you use the proper generic type signature for the outer hashmap.

HashMap<String, HashMap<String, Foo>> hm = new HashMap<String, HashMap<String, Foobar>>();
// populate the map
hm.get("keyname").get("nestedkeyname");

If you're not using generics, you'd have to do a cast to convert the object retrieved from the outer hash map to a HashMap (or at least a Map) before you could call its get() method. But you should be using generics ;-)


import java.util.*;
public class MyFirstJava {
public static void main(String[] args)
{
  Animal dog = new Animal();
  dog.Info("Dog","Breezi","Lab","Chicken liver");
  dog.Getname();
  Animal dog2= new Animal();
  dog2.Info("Dog", "pumpkin", "POM", "Pedigree");
  dog2.Getname();
  HashMap<String,  HashMap<String, Object>> dogs = new HashMap<>();
  dogs.put("dog1", new HashMap<>() {{put("Name",dog.name); 
  put("Food",dog.food);put("Age",3);}});
  dogs.put("dog2", new HashMap<>() {{put("Name",dog2.name); 
  put("Food",dog2.food);put("Age",6);}});
  //dogs.get("dog1");
  System.out.print(dogs + "\n");
  System.out.print(dogs.get("dog1").get("Age"));

} }


You can get the nested value by repeating .get(), but with deeply nested maps you have to do a lot of casting into Map. An easier way is to use a generic method for getting a nested value.

Implementation

public static <T> T getNestedValue(Map map, String... keys) {
    Object value = map;

    for (String key : keys) {
        value = ((Map) value).get(key);
    }

    return (T) value;
}

Usage

// Map contents with string and even a list:
{
  "data": {
    "vehicles": {
      "list": [
        {
          "registration": {
            "owner": {
              "id": "3643619"
            }
          }
        }
      ]
    }
  }
}
List<Map> list = getNestedValue(mapContents, "data", "vehicles", "list");
Map first = list.get(0);
String id = getNestedValue(first, "registration", "owner", "id");

As others have said you can do this but you should define the map with generics like so:

Map<String, Map<String, String>> map = new HashMap<String, Map<String,String>>();

However, if you just blindly run the following:

map.get("keyname").get("nestedkeyname");

you will get a null pointer exception whenever keyname is not in the map and your program will crash. You really should add the following check:

String valueFromMap = null;
if(map.containsKey("keyname")){
  valueFromMap = map.get("keyname").get("nestedkeyname");
}

Yes.

See:

public static void main(String args[]) {

    HashMap<String, HashMap<String, Object>> map = new HashMap<String, HashMap<String,Object>>();
    map.put("key", new HashMap<String, Object>());
    map.get("key").put("key2", "val2");

    System.out.println(map.get("key").get("key2"));
}