【问题标题】:java-8 Stream of Map.entry to SortedMap with a custom comparator带有自定义比较器的 Map.entry 到 SortedMap 的 java-8 流
【发布时间】:2016-12-30 23:29:20
【问题描述】:

此方法接受一个所有值都等于 null 的 Map,并返回一个具有相同键的 SortedMap,带有新值(通过 objectiveFitness 获得)

步骤 1. 首先,我从输入 Map 中取出键,并用相同的键构造一个新的 HashMap,新的值是objectiveFitness(key)。

public SortedMap<Integer[], Integer> evaluate(Map<Integer[], Integer> population, Integer[] melody, Integer[] mode) { 

    Map<Integer[], Integer> fitPop = new HashMap<>();
    fitPop = population.keySet() //you just have the keys.
            .stream()
            .collect(Collectors.toMap(p -> p, p -> this.objectiveFitness(p))); 

第 2 步。下一步是使用 Stream 将 HashMap 中的所有条目收集到具有自定义 Comparator 的 SortedMap 中。

从 Oracle 网站阅读此内容后:http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html

...我发现,由于我想根据条目的自然顺序以外的其他内容来维护 SortedMap 的排序性,因此我需要实现一个包含 2 个部分的 Comparator。我将根据适应度值进行排序,使用键来比较唯一性。 *另外,我想要 2 个值可以相似的情况,但我不想

其中一部分与排序有关,并根据值返回(1,0 或 -1)。比较器的另一部分与唯一性有关(因为地图不允许重复。这是迄今为止我最好的镜头,但我很挣扎。

    Comparator<Map.Entry<Integer[], Integer>> fitnessOrder = 
                                new Comparator<Map.Entry<Integer[], Integer>>() {
            public int compare(Map.Entry<Integer[], Integer> m1, Map.Entry<Integer[], Integer> m2) {
            int fitCmp = m2.getValue().compareTo(m1.getValue());
            if (fitCmp != 0)
                return fitCmp;


            if(m1.getKey().equals(m2.getKey())) return 0;
                for(int i = 0; i < m1.getKey().length; i++){
                    if(m1.getKey()[i] > m2.getKey()[i]){
                        return 1;
                    }
                    if(m1.getKey()[i] < m2.getKey()[i]){
                        return -1;
                }
            }
            return 0;
            }
            };

这看起来与equals一致吗?我真的不知道如何实现它。

如果正确,我想使用上面的 Comparator 来帮助我使用 lambdas 收集到 TreeMap,但是我只是一遍又一遍地卡住。

我也看了:Java TreeMap Comparator 我看到了有关基于键对 SortedMap 进行排序的 cmets,因为它使用 NavigableMap 并因此对键进行排序,否则通过比较器进行排序。不使用 SortedSet 真的没有好办法吗?

    SortedMap<Integer[], Integer> sortedFitPop = fitPop.entrySet()
            .stream()
          //now I want to insert entries into the TreeMap
          //with sortedness according to the Comparator above
            .collect(Collectors.toCollection((k,v) -> (k,v), new TreeMap(fitnessOrder)
   ));

键应该仍然是键,值应该仍然是它们在 HashMap 中的值,但是现在在收集时,我希望 TreeMap 应该始终从头开始排序,并且在放置每个条目之后。

& 是的,当然最好不要收集到 HashMap 中开始,我觉得有一种方法可以使用 Java-8/streams/lambdas 来干净地完成它。

提前感谢您!我好想知道这些东西!

【问题讨论】:

  • 请重新表述您的排序标准。
  • 理想情况下,我想返回一个 SortedMap,其中 Map 有一个比较器,根据 Integer 值排序,而不是 Integer[] 键。
  • 先看HERE。您不能使用TreeMap 按值对Map 进行排序,因为它是按键排序的。您应该对您的条目进行排序并将其放在一个LinkedHashMap 中,它会记住插入的顺序。

标签: lambda java-8 java-stream


【解决方案1】:

作为already said by Flown,不可能创建一个按值排序的TreeMap。想想看。当地图需要查找的结果来确定其在地图中的位置时,它应该如何实现查找?只有当所有地图操作都降级为对整个地图的线性搜索或更糟时,这才会起作用。

另一个问题你自己已经提到过:与equals 的一致性。使用映射值的比较器不能与键的equals 一致,但即使专用于Integer[] 键的比较器也不能与equals 一致,因为Java 数组没有@ 987654327@ 方法。这在使用时也会让您印象深刻,例如LinkedHashMap 并在插入前按值排序。在这种情况下,查找将仅适用于相同的数组对象实例,而不是等效的元素序列,因为数组没有适当的 hashCodeequals 实现。

这是要考虑的一点,无论如何你都不应该使用Integer[] 数组。您可能对泛型不支持原始类型这一事实感到困惑,但是原始类型的 数组 不是原始类型。所以没有理由不在这里使用int[]

当使用int[] 数组时,您可以使用IntBuffer 来包装它们,从而获得一个基于int[] 的内容一致地实现ComparablehashCodeequals 的类型。然后,您可以从已排序的流中创建 LinkedHashMap。只要您稍后不修改地图,它将反映流元素的遇到顺序,这将是所需的顺序。

// convert int[] arrays to IntBuffer via IntBuffer.wrap first
public Map<IntBuffer, Integer> evaluate(Map<IntBuffer, Integer> population, …) {
  Map<IntBuffer, Integer> fitPop = population.keySet().stream()
    .map(ia -> new AbstractMap.SimpleImmutableEntry<>(ia, objectiveFitness(ia.array())))
    .sorted(Map.Entry.<IntBuffer,Integer>comparingByValue()
            .thenComparing(Map.Entry.comparingByKey()))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
            (a,b)->{ throw new IllegalStateException(); }, LinkedHashMap::new));
  return fitPop;
}

看到这里,您可能会想,从整数数组到整数的映射是否真的是人口的适当表示。当您为持有身份标准和当前健康度的人口成员创建专用类型时,您可以摆脱所有这些障碍。这些人口成员的简单列表或数组就足以代表人口。重新计算适应度将是一个简单的forEach 操作,并且根据适应度属性对列表或数组进行排序也很容易(你不需要考虑具有相同适应度的元素的排序,因为没有问题数组或列表中具有相同适应度的元素)。

【讨论】:

  • Holger,感谢您的详细解释(并提醒我使用 int[])。
  • 我最初的决定是创建一个包含字段 Integer[] 注释和 int 适应度的类。对于某些背景:这是实现功能接口的抽象方法,它是我正在编写的遗传算法的子组件。该算法使用 6 种不同功能接口的实现,其中集合在整个时间内传递。到目前为止,objectiveFitness 是计算量最大的任务,因此计算一次是理想的。但是,我同意你的观点,我认为随着我扩展算法
  • 继续...随着我扩展以包含更多适应度标准并可能添加另一层表示,有一个类来处理而不是像我所做的那样可能会很有用。
  • 拥有通过函数接口表示的适应度函数并不能阻止普通类持有参数集和函数评估的最后结果。你不需要把所有东西都放到函数式接口中,只要你想抽象的东西。这可以与委托结合使用。
  • 看来,你有一个想法,但在一个评论中并不容易描述,但无论如何也没有必要。尝试实现你的想法,只有当你遇到问题时,你可能会打开一个新问题,然后用代码示例......
猜你喜欢
  • 2015-11-02
  • 1970-01-01
  • 2011-02-14
  • 2021-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-25
  • 1970-01-01
相关资源
最近更新 更多