我需要使用自定义比较器对列表进行排序:Collections.sort(availableItems, new TextClassifyCnnComparator(citem, false))
class TextClassifyCnnComparator implements Comparator<Item> {
private Item citem;
private boolean isAsc;
public TextClassifyCnnComparator(Item citem) {
this(citem, true);
}
public TextClassifyCnnComparator(Item citem, boolean isAsc) {
this.citem = citem;
this.isAsc = isAsc;
}
private Double calcSimilarScore(Item item) {
return item.getEncodedFromCNN().dotProduct(citem.getEncodedFromCNN());
}
@Override
public int compare(Item o1, Item o2) {
if (isAsc) {
return calcSimilarScore(o1).compareTo(calcSimilarScore(o2));
}
return calcSimilarScore(o2).compareTo(calcSimilarScore(o1));
}
}
Java将为每个项映射并调用
calcSimilarScore
1次还是将其多次调用(为每个元组2项调用1次)?如果调用多次,如何优化此任务?
=========更新1:===============
我在此折射了我的比较器:
class TextClassifyCnnComparator implements Comparator<Integer> {
private boolean isAsc;
private List<Double> list;
public TextClassifyCnnComparator(Item citem, List<Item> list) {
this(citem, list, true);
}
public TextClassifyCnnComparator(Item citem, List<Item> list, boolean isAsc) {
this.list = list.parallelStream().map(item -> calcSimilarScore(item, citem)).collect(Collectors.toList());
this.isAsc = isAsc;
}
private Double calcSimilarScore(Item item1, Item item2) {
return item1.getEncodedFromCNN().dotProduct(item2.getEncodedFromCNN());
}
public List<Integer> createIndexes() {
List<Integer> indexes = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
indexes.add(i); // Autoboxing
}
return indexes;
}
@Override
public int compare(Integer index1, Integer index2) {
// Autounbox from Integer to int to use as array indexes
if (isAsc)
return list.get(index1).compareTo(list.get(index2));
return list.get(index2).compareTo(list.get(index1));
}
}
...
TextClassifyCnnComparator comparator = new TextClassifyCnnComparator(citem, availableItems);
List<Integer> indexes = comparator.createIndexes();
Collections.sort(indexes, comparator);
return indexes.parallelStream().map(index -> availableItems.get(index)).collect(Collectors.toList());
我认为仍然可以进一步优化。
最佳答案
有以下优化:
在可行的情况下,应使用double
(“原始”数据类型)而不是Double
(持有双精度的对象包装器类)。
所比较的citem
中的一部分可以在构造函数中预先计算。 (citem
甚至可能不再需要作为字段。)
一个值可能会被比较多次,因此可能会发生缓存,即从Item到其double值的映射。
所以
class TextClassifyCnnComparator implements Comparator<Item> {
private final Item citem;
private final boolean isAsc;
private final ECNN encodedFromCNN;
private Map<Item, Double> scores = new HashMap<>();
public TextClassifyCnnComparator(Item citem) {
this(citem, true);
}
public TextClassifyCnnComparator(Item citem, boolean isAsc) {
this.citem = citem;
this.isAsc = isAsc;
encodedFromCNN = citem.getEncodedFromCNN();
}
private double calcSimilarScore(Item item) {
Double cached = scores.get(item);
if (cached != null) {
return cached;
}
double score = item.getEncodedFromCNN().dotProduct(encodedFromCNN);
scores.put(Item, score);
return score;
}
@Override
public int compare(Item o1, Item o2) {
if (isAsc) {
return calcSimilarScore(o1).compareTo(calcSimilarScore(o2));
}
return calcSimilarScore(o2).compareTo(calcSimilarScore(o1));
}
}
还是Java 8中的时尚:
private double calcSimilarScore(Item item) {
return scores.computeIfAbsent(item,
it -> it.getEncodedFromCNN().dotProduct(encodedFromCNN));
}