0

So I thought I could use a HashMap for this but it won't work as the first key won't be unique - what makes it unique in the data set is the combination of key 1 and key 2.

I thought about concatenating the keys into a string to force uniqueness, which I think should work, but I wanted to confirm there isn't any other way. To be clear, I was hoping for a data structure that I could...

  1. Look up the first key
  2. Look up the second key
  3. Access the double stored

This works in a nested HashMap but since the first key will not be unique it gets updated with the new second key and double in the next iteration.

From searching, it seems like one suggestion is to use Multimap from Apache's library but I don't want a list to be returned when accessing the first key. I ultimately just want the double by accessing the first key then the second key.

Any suggestions? Or should I try to get concatenation to work?

Sample code

if(map.get(first[1]) != null &&
     map.get(first[1]).get(second[1]) != null) {

     HashMap<String, Double> inner = map.get(first[1]);
     inner.put(second[1], inner.get(second[1]) + 1.0);

} else {

     map.put(first[1], new HashMap<>());
     map.get(first[1]).put(second[1], 1.0);
}

The AND check isn't necessary actually - should just be the second condition

cpd1
  • 777
  • 11
  • 31
  • You said double, but that map holds Integers, and there are some problems, there. You are putting the first key (for the outer map) into the inner map, and you insert a new HashMap if the outer map already has one just because the inner map doesn't have the second key, thus losing all the previous information. – David Conrad Feb 24 '17 at 06:24
  • Sorry, all of that were typos. Double / Integer, either is fine. Ultimately, just trying to update and access the values. first[1] the inner.put should have definitely been second[1]. – cpd1 Feb 24 '17 at 06:29
  • There's something else you didn't mention. It looks like you aren't trying to insert a specific value, but increment a counter? – David Conrad Feb 24 '17 at 06:32
  • The easiest way to do that would be with `computeIfAbsent` and `getOrDefault`, methods on `Map` that were only added in Java 8. Can you use those, or do you need to work with Java 7 or earlier? – David Conrad Feb 24 '17 at 06:34
  • Oh, it's sample code. I modified it a little from what I actually have which is why I had the typos. Manipulating the values aren't an issue, though. It was at first till I read that I needed to get the inner map out of the hash map to get the value. I'll look at the two methods you mentioned. I'm not familiar with them but I don't have any restrictions in terms what Java version I can use so I'll try it out. This is what I was reading... http://stackoverflow.com/questions/1669885/what-happens-when-a-duplicate-key-is-put-into-a-hashmap before I posted which seems like the issue. – cpd1 Feb 24 '17 at 06:38

1 Answers1

0

You don't want a multimap. Your first instinct was right that you can do it as a Map<FirstKey, Map<SecondKey, Integer>>. But to insert into it you'll have to take into account the possibility that the inner map for the FirstKey may or may not have been created.

Based on your edits, I think you want to count the number of times the second key has been inserted into the map, rather than associate a particular value with it. I've rewritten this answer to take that into account.

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

public void insert(Map<String, Map<String, Integer>> map, String first, String second) {
    Map<String, Integer> inner = map.computeIfAbsent(first, k -> new HashMap<>());
    inner.put(second, inner.getOrDefault(second, 0) + 1);
}

public int lookup(Map<String, Map<String, Integer>> map, String first, String second) {
    return map.getOrDefault(first, new HashMap<>()).getOrDefault(second, 0);
}

If you want to use Double instead of Integer it's an easy change, but Integer is more natural if we are just counting occurrences.

David Conrad
  • 15,432
  • 2
  • 42
  • 54
  • Thanks David! Yeah, I noticed that I needed to account for a new HashMap. But wouldn't this be an issue if the first key is not unique? Like if later on there is another "foo" but with a second key as "zoo". I'll lose "foo / bar" – cpd1 Feb 24 '17 at 05:53
  • @cpd1 No you won't. The outer map will contain a map for "foo" that will contain two keys, "bar" and "zoo". – David Conrad Feb 24 '17 at 06:02
  • ok I'll have a look at my code again. as I iterated through the changes, I saw the key getting replaced with the new values. – cpd1 Feb 24 '17 at 06:04
  • @cpd1 How were you inserting it? If you always insert a fresh map, then it will lose the earlier values. You have to either conditionally add it (only if it's not there), or use `computeIfAbsent` (which does the same). `computeIfAbsent` was added in Java 8; if you need to support earlier versions you'll need slightly more verbose code, with an `if` statement. – David Conrad Feb 24 '17 at 06:05
  • I've added the main part that updates / adds to the map – cpd1 Feb 24 '17 at 06:12
  • I'm not sure if I was handling it correctly but it doesn't seem like I can have a duplicate key - just keeps getting overwritten. I ended up switching to using Apache's implementation of tuples to use a Pair class for a key. That seems to be doing what I need it to do...for now anyway. Thanks for the help though – cpd1 Feb 25 '17 at 05:03
  • @cpd1 Did you use the code from my answer? By the way, if I helped you you could show it by upvoting. – David Conrad Feb 27 '17 at 14:42