2
HashMap<ArrayList<Integer>, String>

Iterating though this structure, I get:

[1 1 0], "gh"
[0 4 2], "gh"
[0 5 2], "qh"

However, I want to sort this on the key (in increasing order), to get

[0 4 2], "gh"
[0 5 2], "qh"
[1 1 0], "gh"

The first component is sorted first, then the second, then the third.

  • 1
    Is that good practice to have arraylist as key? I would suggest http://arraylist.blogspot.com/2011/12/where-to-use-identityhashmap.html – kosa Dec 09 '12 at 05:51
  • Generally speaking, no it isn't. Your keys should be immutable. `http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html` might be of interest too. – Daniel B. Chapman Dec 09 '12 at 05:53
  • @DanielChapman `LinkedHashMap` preserves _insertion_ order, not key-based ordering. – Matt Ball Dec 09 '12 at 06:00
  • Agree with @Nambari (+1), its not a good approach to have arraylist as key in hashMap. Better put arraylist in a custom object and override hashcode, equals method and also implement comparable interface. – rai.skumar Dec 09 '12 at 06:19

2 Answers2

4

HashMaps have no inherent ordering.

This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

Use a different data structure, or externally sort the keys yourself, before iterating. Either way, you'll need to write a Comparator<List<Integer>> since List<E> does not extend Comparable<List<E>>

List<List<Integer>> keys = new ArrayList<>(map.keySet());
Collections.sort(keys, new Comparator<List<Integer>>() {
    @Override public int compare(List<Integer> a, List<Integer> b) {
        // TODO handle edge cases, like lists of differing size

        for (int i=0; i<Math.min(a.size(), b.size()); i++) {
            int result = Integer.compare(a.get(i), b.get(i));
            if (result != 0) {
                return result;
            }
        }

        return 0;
    }
});

for (List<Integer> key : keys) {
    String value = map.get(key);
    // do something interesting with the value
}

N.B. it is generally a Bad Idea to use mutable keys in a map of any type. Personally, I've never had a need to – at least use an immutable list of integers instead.

Community
  • 1
  • 1
Matt Ball
  • 332,322
  • 92
  • 617
  • 683
  • Once I have defined your collections.sort in my class, I'm not sure how I should call it after HashMap, String> map = new HashMap, String>(); . Can I simply do map.sort()? – Wuschelbeutel Kartoffelhuhn Dec 09 '12 at 06:19
  • No, read my code more carefully. You sort a list of the keys, immediately before iterating on the keys & values of the map. Also, the fact that there is no `map.sort()` method should be a pretty good hint about the answer to your comment, don't you think? – Matt Ball Dec 09 '12 at 06:22
  • I have never seen this syntax before. Is your block Collections.sort(... a function that can be called or does it literally sort the data after that code is executed? Furthermore, if I use your code, can I declare my map as before or does your map.keySet() require that I instantiate it differently? Thanks – Wuschelbeutel Kartoffelhuhn Dec 09 '12 at 06:26
  • The `Collections.sort()` bit is a function call to this method: http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#sort%28java.util.List,%20java.util.Comparator%29. It passes an [anonymous inner class](http://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html) of `Comparator` as the second argument. You can declare the map as before – [every map implementation supports the `keySet()` method](http://docs.oracle.com/javase/7/docs/api/java/util/Map.html#keySet()). – Matt Ball Dec 09 '12 at 06:29
  • But I do have to call your override Collections.sort() with keys, right before I iterate though all the keys, right? And this is what's confusing for me because sort has two args and I dont know what to supply...why cant i do keys.sort()...sorry for the elementary question (im a beginner) – Wuschelbeutel Kartoffelhuhn Dec 09 '12 at 06:36
  • Yes to the first sentence, though your terminology usage is a bit off (there is no "override" here). `sort()` takes two args and **my code shows you exactly what to pass:** a list of the keys, and a comparator that describes how to sort those keys. You can't do `keys.sort()` because there is no such method: http://docs.oracle.com/javase/7/docs/api/java/util/List.html. Side note, I really hope you're actually clicking these JavaDoc links. – Matt Ball Dec 09 '12 at 06:40
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/20829/discussion-between-matt-ball-and-wuschelbeutel-kartoffelhuhn) – Matt Ball Dec 09 '12 at 06:47
0

TreeMap may be useful for you. There are a few ways to handle this. I would personally not use an ArrayList as the index to a Map. Instead, I'd fabricate a key from the ArrayList and include the actual ArrayList in the value.

In the case above, the value of the Map would be of this type:

class SomeClass() {
    ArrayList<Integer> aList;
    String aString;  // gh and qh in your example.
    public Integer getKey() {
        // Do something here to create a suitable key from aList and return it.
    } 
}

(Structurally, this sort of class is probably more of what you want anyway - you don't want the List and the String separated if they are related, which they clearly seem to be.)

Now you'd make a TreeMap not a HashMap and add SomeClass instances:

TreeMap treeMap = new TreeMap();
SomeClass someClass = new SomeClass(...); // construct it somehow
treeMap.add(someClass.getKey(), someClass);

And Bob's your uncle.

Tony Ennis
  • 10,958
  • 6
  • 46
  • 68