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);
        }
    }
}
17
  • Arrays are not suitable as keys for Maps, so I'd start by changing the key to List<Integer> Commented Feb 9, 2020 at 10:39
  • From the documentation of HashMap: "...This class makes no guarantees as to the order of the map..." Commented Feb 9, 2020 at 10:40
  • @Turing85 OK, I updated my question a little bit by changing HashMap to LinkedHashMap so it would met the requirements ;-) Commented Feb 9, 2020 at 10:41
  • 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 Commented Feb 9, 2020 at 11:02
  • 1
    Your keys are lists of length 2. How do you want to compare them with each other? LinkedList doesn't implement Comparable. Commented Feb 9, 2020 at 12:58

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

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!
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;

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.