1

I want to sort LinkedHashMap that has LinkedList<Integer> as key and float[] as value.

For example let's say I have such LinkedHashMap like this:

LinkedHashMap<LinkedList<Integer>, float[]> hm = new LinkedHashMap<>();
LinkedList<Integer> linkedlist;

linkedlist = new LinkedList<>();
linkedlist.add(10);
linkedlist.add(7);
hm.put(linkedlist, new float[]{0.14f, 1.2f, 85.01f});

linkedlist = new LinkedList<>();
linkedlist.add(0);
linkedlist.add(41);
hm.put(linkedlist, new float[]{10.3f, 50.05f, 9.9f});

linkedlist = new LinkedList<>();
linkedlist.add(210);
linkedlist.add(3);
hm.put(linkedlist, new float[]{17.0f, 4.0f, 2.1f});

Now what I want is the output to be:

{0, 41}, {10.3f, 50.05f, 9.9f}

{10, 7}, {0.14f, 1.2f, 85.01f}

{210, 3}, {17.0f, 4.0f, 2.1f}

But when I use suggested solution (suggested by some of you in the comments section saying it is duplicate of another post that already have the right answer - the one below, link to it here) where it created new LinkedHashMap (cos I do not know how to sort the original LinkedHashMap in place by this code, unfortunately) like this:

LinkedHashMap<LinkedList<Integer>, float[]> sortedMap = new LinkedHashMap<>();
hm.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByKey())
    .forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue()));
hm = sortedMap;

My NetBeans 8.0.2 show me error about the line .sorted(Map.Entry.comparingByKey()) saying:

incompatible types: inference variable K has incompatible bounds equality constraints: LinkedList<Integer> upper bounds: Comparable <? super K> where K,V are type-variables: K extends Comparable<? super K> declared in method <K,V>comparingByKey() V extends Object declared in method <K,V>comparingByKey()

I thought maybe for some reason the values in the LinkedList are wrong so I tested it like this and it shows those are all correct (I tested just first 50 entries as the list have hundreds of them):

for (int i = 0; i < hm.size(); i++) {
    if (i < 50) {
        for (Map.Entry<LinkedList<Integer>, float[]> entry : hm.entrySet()) {
            LinkedList<Integer> k = entry.getKey();
            System.out.println(k);
        }
    }
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
qraqatit
  • 492
  • 4
  • 14
  • Arrays are not suitable as keys for Maps, so I'd start by changing the key to `List` – Eran Feb 09 '20 at 10:39
  • From [the documentation of `HashMap`](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/HashMap.html): "*...This class makes no guarantees as to the order of the map...*" – Turing85 Feb 09 '20 at 10:40
  • @Turing85 OK, I updated my question a little bit by changing HashMap to LinkedHashMap so it would met the requirements ;-) – qraqatit Feb 09 '20 at 10:41
  • @Eran would that preserve the order of placed Integers? I need it to be in the exact order as placed into array/List – qraqatit Feb 09 '20 at 10:44
  • This is a duplicate question. https://stackoverflow.com/questions/15576009/how-to-make-hashmap-work-with-arrays-as-key – Joe Feb 09 '20 at 10:50
  • @Joe but that post you linked is not about sorting LinkedHashMap which key is int[], so how can it be a duplicate? – qraqatit Feb 09 '20 at 10:54
  • @qraqatit, it's similar because the person is trying to create a key out of an array and discusses the issues and how one could possible get around them. – Joe Feb 09 '20 at 10:55
  • @Joe I still don't see how that can answer my specific question tho so it leaves me without actual answer – qraqatit Feb 09 '20 at 10:58
  • 1
    I feel as though it's spelled out in the answer to that question. However, as the answer says You cannot do it this way. Both t and a will have different hashCode() values because the the java.lang.Array.hashCode() method is inherited from Object, which uses the reference to compute the hash-code (default implementation). Hence the hash code for arrays is reference-dependent, which means that you will get a different hash-code value for t and a. Furthermore, equals will not work for the two arrays because that is also based on the reference.Granted you may have one array but some concept – Joe Feb 09 '20 at 11:02
  • @Joe OK, so because admins closed this question as already answered elsewhere which does not work for me and because of yours and Erans answers I will asked it differently by changing key of int[] to LinkedList, hope this time it will be OK to ask like that – qraqatit Feb 09 '20 at 11:06
  • Collections.sort(id) Collections.sort(Comparator.compare(id)) Collections.sort(Comparator.compare(id).reversed() Collections.sor(list,customComparator()) – Joe Feb 09 '20 at 11:16
  • @OleV.V. I would but it says I have to wait another 90 minutes before posting new question - or can I edit this one? – qraqatit Feb 09 '20 at 11:51
  • @OleV.V. OK, I just did so - I edited/updated my question... – qraqatit Feb 09 '20 at 12:52
  • 1
    Your keys are lists of length 2. How do you want to compare them with each other? `LinkedList` doesn't implement `Comparable`. – kaya3 Feb 09 '20 at 12:58
  • @OleV.V. thanks - I hope someone can correct that one line in that "comparing" section (well, if it turns out to be some minor mistake or something) – qraqatit Feb 09 '20 at 12:58
  • @kaya3 do not ask me, I just used suggested solution when I was told it is basically a duplicate, so I did and it not works and as I am not that skilled to tell why that is the reason I am asking my question – qraqatit Feb 09 '20 at 12:59
  • So you found code somewhere else, but you don't understand it and it doesn't work for your problem? I don't get it. What are you actually trying to do? – kaya3 Feb 09 '20 at 13:01

2 Answers2

1

The problem with this approach is that the key you have, a LinekdList<Integer> is not Comparable. You can overcome this by providing a custom Comparator.

A common way to implement such comparators is to compare the integers in matching places, until you hit a pair that aren't equal. If one of the lists is shorter than the other and you didn't hit a non-matching element in it before its end, you can define it as "smaller" than the longer list:

Comparator<LinkedList<Integer>> cmp = new Comparator<LinkedList<Integer>>() {
    @Override
    public int compare(LinkedList<Integer> o1, LinkedList<Integer> o2) {
        Iterator<Integer> i1 = o1.iterator();
        Iterator<Integer> i2 = o2.iterator();

        // Iterate over the first list
        while (i1.hasNext()) {
            Integer x1 = i1.next();

            // If the second list has no more elements, it's "smaller"
            if (!i2.hasNext()) {
                return 1;
            }

            Integer x2 = i2.next();
            int cmp = Integer.compare(x1, x2);
            if (cmp != 0) {
                // If the matching items aren't equal, we know which list is "smaller"
                return cmp;
            }
        }

        // Done going over the first list.
        // If the second one still has items, it's "bigger"
        if (i2.hasNext()) {
            return -1;
        }

        // If not, both lists are equal
        return 0;
    }
};

Once you have that comparator available, you just need to use it for sorting the map's keys:

LinkedHashMap<LinkedList<Integer>, float[]> sortedMap = new LinkedHashMap<>();
hm.entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey(cmp)) // Here
        .forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue()));
hm = sortedMap;
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • It’s more general than my solution, comparing the entire list (where I compare only the first element). For most purposes I guess this will be preferable, but whether it’s worth the extra code lines, everyone will have to judge by themselves. Your comments in the code are great for explaining what is going on. – Ole V.V. Feb 09 '20 at 13:32
1

The simple fix: You said that you wanted sorting by the first (0th) element of the list. So specify that:

    hm.entrySet()
        .stream()
        .sorted(Comparator.comparing(me -> me.getKey().get(0)))
        .forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue()));

With this sorting let’s try writing out the sorted map afterwards, for example:

    sortedMap.forEach((k, v) -> System.out.println("" + k + " -> " + Arrays.toString(v)));
[0, 41] -> [10.3, 50.05, 9.9]
[10, 7] -> [0.14, 1.2, 85.01]
[210, 3] -> [17.0, 4.0, 2.1]

If you didn’t want to create a new map, you must remove each key before inserting it again for the order to be changed:

    hm.entrySet()
        .stream()
        .sorted(Comparator.comparing(me -> me.getKey().get(0)))
        .forEachOrdered(x -> {
            hm.remove(x.getKey());
            hm.put(x.getKey(), x.getValue());
        });

    hm.forEach((k, v) -> System.out.println("" + k + " -> " + Arrays.toString(v)));

Now the output is the same as above.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • YES! This seems to be working and it is actually the most easiest to apply to my actual code - thank you so much + the best part is it in fact made the sorting "in place" that is without creating new LinkedHashMap, perfect! – qraqatit Feb 09 '20 at 13:27