【问题标题】:Sort LinkedHashMap with LinkedList<Integer> as key以 LinkedList<Integer> 为键对 LinkedHashMap 进行排序
【发布时间】:2020-05-24 21:40:32
【问题描述】:

我想对以LinkedList&lt;Integer&gt; 为键、float[] 为值的 LinkedHashMap 进行排序。

例如,假设我有这样的 LinkedHashMap:

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});

现在我想要的是输出:

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

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

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

但是当我使用建议的解决方案时(你们中的一些人在 cmets 部分建议说它是另一个已经有正确答案的帖子的重复 - 下面的帖子,链接到它here)它创建了新的 LinkedHashMap (因为我不知道如何通过这段代码对原始的 LinkedHashMap 进行排序,不幸的是)像这样:

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

我的 NetBeans 8.0.2 向我显示关于 .sorted(Map.Entry.comparingByKey()) 行的错误:

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()

我认为可能由于某种原因 LinkedList 中的值是错误的,所以我这样测试它,它表明这些都是正确的(我只测试了前 50 个条目,因为列表中有数百个条目):

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);
        }
    }
}

【问题讨论】:

  • 数组不适合作为 Maps 的键,所以我先将键更改为 List&lt;Integer&gt;
  • 来自the documentation of HashMap: "...此类不保证地图的顺序..."
  • @Turing85 好的,我通过将 HashMap 更改为 LinkedHashMap 稍微更新了我的问题,这样它就可以满足要求 ;-)
  • 我觉得这个问题的答案中已经说明了这一点。但是,正如答案所说,您不能这样做。 t 和 a 都将具有不同的 hashCode() 值,因为 java.lang.Array.hashCode() 方法继承自 Object,它使用引用来计算哈希码(默认实现)。因此,数组的哈希码是依赖于引用的,这意味着您将获得 t 和 a 的不同哈希码值。此外,equals 不适用于两个数组,因为这也是基于引用。当然,您可能有一个数组,但有一些概念
  • 您的键是长度为 2 的列表。您想如何将它们相互比较? LinkedList 没有实现 Comparable

标签: java arrays sorting hashmap


【解决方案1】:

这种方法的问题在于您拥有的密钥LinekdList&lt;Integer&gt; 不是Comparable。您可以通过提供自定义 Comparator 来克服这个问题。

实现此类比较器的常用方法是比较匹配位置的整数,直到找到不相等的对。如果其中一个列表比另一个短,并且您没有在其结束之前点击其中的不匹配元素,则可以将其定义为比较长的列表“更小”:

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

一旦你有了那个比较器,你只需要使用它来对地图的键进行排序:

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;

【讨论】:

  • 它比我的解决方案更通用,比较整个列表(我只比较第一个元素)。对于大多数目的,我想这会更可取,但是否值得额外的代码行,每个人都必须自己判断。代码中的 cmets 非常适合解释发生了什么。
【解决方案2】:

简单的解决方法:您说您希望按列表的 第一个(第 0 个)元素进行排序。所以指定:

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

通过这种排序,让我们尝试写出排序后的地图,例如:

    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]

如果您不想创建新地图,则必须在重新插入之前删除每个键以更改顺序:

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

现在输出和上面一样了。

【讨论】:

  • 是的!这似乎是有效的,它实际上是最容易应用于我的实际代码的 - 非常感谢 + 最好的部分是它实际上在没有创建新的 LinkedHashMap 的情况下进行了“就地”排序,完美!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-23
  • 1970-01-01
  • 2016-03-10
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 2012-09-20
相关资源
最近更新 更多