此方法获取一个所有值都等于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使用自定义的Comparator将HashMap中的所有条目收集到SortedMap中。
从Oracle网站阅读以下内容:http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
...我发现,由于我想基于除条目自然排序之外的其他方法来维持SortedMap的排序性,因此我需要实现一个由两部分组成的Comparator。我将根据适应度值进行排序,使用该键比较唯一性。 *此外,我希望2个值可以相似,但是我不想
其中一部分与排序有关,并根据值返回(1,0或-1)。比较器的另一部分与唯一性有关(因为Maps不允许重复。这是到目前为止我最好的镜头,但是我很努力。
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帮助我使用lambda收集到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进行整洁。
先感谢您!我想非常了解这个东西!
最佳答案
作为already said by Flown,无法创建按值排序的TreeMap
。想一想。当 map 需要该查询的结果来确定其在 map 中的位置时,该如何实现查询?仅当所有 map 操作降级为整个 map 的线性搜索或更糟时,这才起作用。
您自己已经提到了另一个问题:与equals
的一致性。使用映射值的比较器不能与键的equals
保持一致,但是即使专用于Integer[]
键的比较器也不能与equals
保持一致,因为Java数组没有equals
方法。这在使用时也会引起您的注意LinkedHashMap
,并在插入前按值排序。在这种情况下,由于数组没有适当的hashCode
或equals
实现,因此查找仅适用于相同的数组对象实例,而不适用于等效的元素序列。
考虑到这一点,您无论如何都不应使用Integer[]
数组。泛型不支持基本类型,但基本类型的数组不是基本类型,这可能使您感到困惑。因此,没有理由不在此处使用int[]
。
使用int[]
数组时,可以使用IntBuffer
对其进行包装,从而获得一种类型,该类型根据Comparable
的内容一致地实现hashCode
,equals
和int[]
。然后,您可以从已排序的流中创建LinkedHashMap
。只要您以后不修改 map ,它就会反映流元素的遇到顺序,这将是所需的顺序。
// 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
操作,并且根据适应度属性对列表或数组进行排序也很容易(您无需考虑具有相同适应度的元素的顺序,因为使用数组或列表中的适应度相同)。