0

I have a multiple-multi-dimensional HashMap() instances, I am using them to store hierarchical data from a database;

HashMap<String, HashMap<String, ArrayList<String>>>

I add to them with 3 primary methods that we'll refer to as addTop(), addMid() and addLow(). The methods all accept parameters that match their data group and a string, each method returns the next dimension of the HashMap();

public static HashMap<String, ArrayList<String>> addTop(HashMap<String, HashMap<String, ArrayList<String>>> data, String val) { ... };
public static ArrayList<String> addMid(HashMap<String, ArrayList<String>> data, String val) { ... };
public static String addLow(ArrayList<String> data, String val) { ... };

I call these, usually, in sequence in between a few checks and perform additional checks inside the methods. Essentially all these methods do is add val to data then return an empty HashMap();

out = new HashMap();
data.put(val, out);
return out;

When I check at the end of the loop/data-population all of the data from addMid() & addLow() is missing. Why is this?

I thought Java worked by reference when dealing with complex objects, such as HashMap().

What can I do to ensure that addMid() and addLow() update the master HashMap()?

EDIT: Included code. It compiles and runs but there are other problems, I have stripped out as much as I can to demonstrate whats happening, except the SQL stuff, that won't compile, sorry. the method that is run at start is sqlToArray();

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

public class Av2 {
    protected class AvailLookup {
        private Integer key;
        private String value;
        public AvailLookup(Integer inKey, String inValue) {
            key = inKey;
            value = inValue;
        }
        public void updateName(String name) {
            value = name;
        }
        public Integer getKey() {
            return key;
        }
        public String getValue() {
            return value;
        }
        public String toString() {
            return value;
        }
    }
    private static HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>> data = new HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>>();
    private static Sql sql = new Sql("PlantAvail");
    public static HashMap<AvailLookup,  ArrayList<AvailLookup>> getChannel(HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>> inArray, Integer channel) {
        HashMap<AvailLookup, ArrayList<AvailLookup>> out = null;
        if (inArray != null ) {
            for (AvailLookup lookup : inArray.keySet()) {
                if (lookup.getKey() == channel) {
                    out = inArray.get(lookup);
                    System.out.println("Channel: " + channel + " found");
                    break;
                }
            }
            if (out == null) {
                System.out.println("Channel: " + channel + " not found");
            }
        }
        return out;
    }
    public static HashMap<AvailLookup,  ArrayList<AvailLookup>> getChannel(HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>> inArray, String channel) {
        HashMap<AvailLookup, ArrayList<AvailLookup>> out = null;
        if (inArray != null ) {
            for (AvailLookup lookup : inArray.keySet()) {
                if (lookup.getValue() != null) {
                    if (lookup.getValue().equalsIgnoreCase(channel)) {
                        out = inArray.get(lookup);
                        System.out.println("Channel: " + channel + " found");
                        break;
                    }
                }
            }
            if (out == null) {
                System.out.println("Channel: " + channel + " not found");
            }
        }
        return out;
    }
    public static HashMap<AvailLookup, ArrayList<AvailLookup>> addChannel(HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>> inArray, Integer id, String name) {
        HashMap<AvailLookup, ArrayList<AvailLookup>> out = null;
        if (inArray != null ) {
            if (getChannel(inArray, id) == null) {
                out = new HashMap<AvailLookup, ArrayList<AvailLookup>>();
                inArray.put(new AvailLookup(id, name), new HashMap<AvailLookup, ArrayList<AvailLookup>>());
                System.out.println("Channel: added " + id);
            } else {
                System.out.println("Channel: " + id + " already exists");
            }
        } else {
            System.out.println("Channel: " + id + " already exists");
        }
        return out;
    }
    public static void removeChannel(HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>> inArray, Integer channel) {
        boolean pass = false;
        HashMap<AvailLookup,  ArrayList<AvailLookup>> channelLookup = getChannel(inArray, channel);
        for (AvailLookup lookup : channelLookup.keySet()) {
            if (lookup.getKey() == channel) {
                inArray.remove(channel);
                System.out.println("Channel: " + channel + " removed");
                pass = true;
                break;
            }
        }
        if (!pass) {
            System.out.println("Channel: " + channel + " cannot be removed");
        }
    }
    public static ArrayList<AvailLookup> getDevice(HashMap<AvailLookup, ArrayList<AvailLookup>> channel, Integer device) {
        ArrayList<AvailLookup> out = null;
        for(AvailLookup lookup : channel.keySet()) {
            if (lookup.getKey() == device) {
                out = channel.get(device);
                System.out.println("Device: " + device + " found");
                break;
            }
        }
        if (out == null) {
            System.out.println("Device: " + device + " not found");
        }
        return out;
    }
    public static ArrayList<AvailLookup> getDevice(HashMap<AvailLookup, ArrayList<AvailLookup>> channel, String device) {
        ArrayList<AvailLookup> out = null;
        for(AvailLookup lookup : channel.keySet()) {
            if (lookup.getValue() == device) {
                out = channel.get(device);
                System.out.println("Device: " + device + " found");
                break;
            }
        }
        if (out == null) {
            System.out.println("Device: " + device + " not found");
        }
        return out;
    }
    public static ArrayList<AvailLookup> addDevice(HashMap<AvailLookup, ArrayList<AvailLookup>> channel, Integer id, String value) {
        ArrayList<AvailLookup> out = null;
        if (getDevice(channel, id) == null) {
            out = new ArrayList<AvailLookup>();
            channel.put(new AvailLookup(id, value), new ArrayList<AvailLookup>());
            System.out.println("Device: added " + id);
        } else {
            System.out.println("Device: " + id + " already exists");
        }
        return out;
    }
    public static void removeDevice(HashMap<AvailLookup, ArrayList<AvailLookup>> channel, Integer device) {
        boolean pass = false;
        ArrayList<AvailLookup> deviceLookup = getDevice(channel,device);
        for (AvailLookup lookup : deviceLookup) {
            if (lookup.getKey() == device) {
                channel.remove(device);
                System.out.println("Device: " + device + " removed");
                pass = true;
                break;
            }
        }
        if (!pass) {
            System.out.println("Device: " + device + " cannot be removed");
        }
    }
    public static AvailLookup getHost(ArrayList<AvailLookup> hosts, Integer host) {
        AvailLookup out = null;
        for (AvailLookup hostLookup : hosts) {
            if (hostLookup.getKey() == host) {
                out = hostLookup;
                System.out.println("Host: " + host + " found");
            }
        }
        if (hosts.contains(host)) {
        } else { 
            System.out.println("Host: " + host + " not found");
        }
        return out;
    }
    public static AvailLookup getHost(ArrayList<AvailLookup> hosts, String host) {
        AvailLookup out = null;
        for (AvailLookup hostLookup : hosts) {
            if (hostLookup.getValue() == host) {
                out = hostLookup;
                System.out.println("Host: " + host + " found");
            }
        }
        if (hosts.contains(host)) {
        } else { 
            System.out.println("Host: " + host + " not found");
        }
        return out;
    }
    public static AvailLookup addHost(ArrayList<AvailLookup> hosts, Integer id, String value) {
        AvailLookup out = null;
        for (AvailLookup hostLookup : hosts) {
            if (hostLookup.getKey() == id) {
                out = hosts.set(id, new AvailLookup(id, value));
                System.out.println("Host: " + id + " found");
                break;
            }
        }
        if (out == null) {
            System.out.println("Host: " + id + " not found");
        }
        return out;
    }
    public static void removeHost(ArrayList<AvailLookup> hosts, Integer host) {
        boolean pass = false;
        for (AvailLookup hostLookup : hosts) {
            if (hostLookup.getKey() == host) {
                hosts.remove(hostLookup);
                System.out.println("Host: " + host + " removed");
                pass = true;
            }
        }
        if (!pass) {
            System.out.println("Host: " + host + " cannot be removed");
        }
    }
    public static ArrayList<AvailLookup> otherHosts(ArrayList<AvailLookup> hosts, Integer key, String value) {
        ArrayList<AvailLookup> out = null;
        for (AvailLookup host : hosts) {
            if (host.getKey() != key) {
                if (out == null) {
                    out = new ArrayList<AvailLookup>();
                }
                out.add(new AvailLookup(key, value));
            }
        }
        if (out != null) {
            if (out.size() > 1) {
                System.out.println("Host: generated other hosts");
            }
        }
        return out;
    }
    public static AvailLookup nextHost(ArrayList<AvailLookup> otherHosts) {
        AvailLookup out = null; 
        if (otherHosts != null) {
            out = otherHosts.get(0);
            System.out.println("Host: getting next host");
        } else {
            System.out.println("Host: no other host");
        }
        return out;
    }
    public static void sqlToArray() {
        HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>> tempData = new HashMap<AvailLookup, HashMap<AvailLookup, ArrayList<AvailLookup>>>();
        Integer iHost = null;
        Integer iDevice = null;
        Integer iChannel = null;
        String sHost = null;
        String sDevice = null;
        String sChannel = null;
        HashMap<AvailLookup, ArrayList<AvailLookup>> channel = null;
        ArrayList<AvailLookup> device = null;
        Sql obj = new Sql("plantavail");
        obj.query("select j_channel.id as channelid, j_channel.name as channelname, j_device.id as deviceid, j_device.name as devicename, j_io.id as hostid, j_io.host as hostname, alias"
                + " from j_io"
                + " left join j_channel on j_io.id = j_channel.iofk"
                + " left join j_device on j_channel.iofk = j_device.id");
        try {
            while(obj.getResult().next()) { 
                sChannel = obj.getResult().getString("channelname");
                sDevice = obj.getResult().getString("devicename");
                sHost = obj.getResult().getString("hostname");
                iChannel = obj.getResult().getInt("channelid");
                iDevice = obj.getResult().getInt("deviceid");
                iHost = obj.getResult().getInt("hostid");
                channel = addChannel(tempData, iChannel, sChannel);
                if (channel != null) {
                    device = addDevice(channel, iDevice, sDevice);
                    if (device != null) {
                        addHost(device, iHost, sHost);
                    }
                }
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        data = tempData;
    }
} 
glend
  • 1,208
  • 1
  • 10
  • 23
  • 6
    Show the data population loop. You may not be passing what you think you're passing. And how can those methods return `out` if `out` is a raw `HashMap` and their return value is `ArrayList(String)` and `String`? You're not showing the real code, show the real code or create an [MCVE](http://stackoverflow.com/help/mcve). – RealSkeptic Jun 09 '15 at 13:58
  • 1
    The example you have posted is not useful. Write a simple and representative example that is **fully working** and that demonstrates your problem. – Boris the Spider Jun 09 '15 at 13:59
  • Java is [always pass by value](http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value). The terminology is just confusing because "pass by reference" and "passing references" are different things. – Radiodef Jun 09 '15 at 14:05
  • I have included the full block of code... riddled with other errors, i'm sure. – glend Jun 09 '15 at 14:31

2 Answers2

3

Be careful with accidentally overriding your existing map values. If you use java 8 you can use:

map.computeIfAbsent("entry", s -> new ArrayList<>());

Before Java 8 you need to check if the value is null:

List<String> list = map.get("entry");

if(list == null){
   list = map.put("entry", new ArrayList<String>());
}

Also you need to make sure that you update your map correctly:

A little example:

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

String a = "a";
String b = "b";

map.put(a, b);

System.out.println(map.get(a));

b = "c";

System.out.println(map.get(a));
System.out.println(b);

The output is:

b
b
c

So you see if you update b the map does not get updated. Now the same thing with a map in a map:

final String a = "a";
final String b = "b";

Map<String, Map<String, String>> topMap = new HashMap<>();
Map<String, String> middleMap = topMap.getOrDefault(a, new HashMap<>());

middleMap.put(b, "c");

topMap.put("a", middleMap);

System.out.println(topMap.get(a).get(b));

middleMap.replace(b, "d");

System.out.println(topMap.get(a).get(b));

topMap.put("a", middleMap);

System.out.println(topMap.get(a).get(b));

The output is:

c
d
d

But why? Shouldn't it be 'c c d'? NO! Because a String in Java is immutable, but a Map is not. If you consider this you should be able to solve your problem.

Julian Pieles
  • 3,450
  • 2
  • 20
  • 30
  • See, I think I considered this. But, I'm not sure how to put the map after creation, given the process flow. Additionally, I don't know how to rewrite the process flow to support it. – glend Jun 09 '15 at 14:45
  • First, and worst of all, you example has rawtypes here `List list` and here `new ArrayList()`. Second, `getOrDefault` will **always** create a `new ArrayList` - this is unlikely what you want to all. You want is [`Map.computeIfAbsent`](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-). – Boris the Spider Jun 09 '15 at 15:32
  • @BoristheSpider The section before Java 8 is just an example to get the point. Anyway I will update it. Your second statement is just wrong. getOrDefault will only create a new Object if the map does not contain the key. The Java 8 API says: "Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key." https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#getOrDefault-java.lang.Object-V- – Julian Pieles Jun 10 '15 at 07:33
  • 1
    @JulianLiebl this is false. You are passing an _object reference_ and **not** a lambda. Therefore the constructor is called and the object created. The reference is then pushed onto the stack. Whether that reference is used or not is neither here nor there, it **must** be created - although actually it is exactly the point, the `List` is created then discarded as the key is already in the `Map`. `computeIfAbsent` takes a **lambda** which is only called when the object is required - this means that the `List` is **only** created if needed. – Boris the Spider Jun 10 '15 at 07:41
  • @BoristheSpider Ah now I see what you mean. This makes sense! Thanks for the info. Still the statement it will always create a new list is not correct or am I wrong again? As I see it if I just want to get the object and avoid a null pointer getOrDefault is a valid use case? – Julian Pieles Jun 10 '15 at 07:44
  • You are wrong. Each time you call the method, the parameters are evaluated. Each time they are evaluated `new ArrayList` is called. `getOrDefault` should be used when you have a _default_ value such as `0` or the empty `String`. – Boris the Spider Jun 10 '15 at 07:46
  • @BoristheSpider Do you have a source for this? I just looked into the source implementation of Map.java: http://hastebin.com/equduqodaf.coffee This is exactly what I thought it would do. It will not create a new Object. – Julian Pieles Jun 10 '15 at 08:06
  • I do not need a source, this is how Java _works_. You are passing in an instance of `V v` therefore it needs to exist. Java is not lazily evaluated so it must be created before the method is called. I don't understand where your confusion comes from! Create custom class and add a print statement to the constructor to verify. – Boris the Spider Jun 10 '15 at 08:08
  • 1
    @BoristheSpider Ah! Now I know what you mean. My confusion was coming from wrong thinking. I thought you mean that the Object which is already in the map gets evaluated/created which would make no sense at all. I was not talking about the default value which ofc gets created everytime. Thank you for being patient. – Julian Pieles Jun 10 '15 at 08:15
  • I updated the example based on BoristheSpider's input. – Julian Pieles Jun 10 '15 at 08:19
1

You need to check if there is already a map for this key:

Map<...> result = data.get(val);
if(null == result) {
    result = new HashMap();
    data.put(val, result);
}
return out;

Without this, the second attempt to add values to the same key will overwrite the existing map instead of appending to it.

Aaron Digulla
  • 297,790
  • 101
  • 558
  • 777