java-8使用自定义比较器

时间:2016-08-24 01:15:28

标签: lambda java-8 java-stream

此方法采用所有值等于null的Map并返回相同键的SortedMap,并使用新值(通过objectiveFitness获取)

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

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一致吗?我无法真正告诉你如何实施它。

如果它是正确的,我想使用上面的比较器来帮助我使用lambdas收集到TreeMap中,但是我只是一遍又一遍地被困住。

我也看了看:Java TreeMap Comparator 我看到有关基于键的SortedMap排序的注释,因为它使用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干净地完成它。

提前谢谢!我很想知道这些东西!

1 个答案:

答案 0 :(得分:1)

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

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

这是要考虑到你不应该使用Integer[]数组的要点。您可能会对Generics不支持基本类型这一事实感到困惑,但基本类型的数组不是基本类型。所以没有理由不在这里使用int[]

使用int[]数组时,您可以使用IntBuffer来包装它们,获取一个实现ComparablehashCodeequals的类型, int[]的内容。然后,您可以从已排序的流中创建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操作,并且根据健身属性对列表或数组进行排序也很容易(您不需要考虑具有相同适应度的元素的排序,因为没有在数组或列表中具有相同适应度的元素的问题。)