我正在尝试使用比较器来帮助对对象列表进行排序。我有一个问题,关于比较器的确切工作原理以及它在以下示例中的作用:
private static Comparator<Student> comparator()
{
return (Student a, Student b) ->
{
return Integer.compare(complexOperation(a), complexOperation(b));
}
}
如您所见,需要根据complexOperation()
方法返回的整数排名对学生进行比较和排序。顾名思义,这是一项繁重的操作。上述方法是否最有效?或者最好是按照我要排序的列表中的每个学生进行排序,对每个学生执行complexOperation()
并将结果存储在Student对象的字段中。那么比较器就会做:
Integer.compare(a.getRank(), b.getRank())
这两种方法是否具有可比性,或者由于比较器的工作方式(也许比较同一个对象不止一次,因此在比较期间每个学生多次运行complexOperation()),这样做会更快吗?在学生领域中预先计算complexOperation()的结果?
以上将被称为:
Collections.sort(students, comparator());
希望很清楚!
编辑: 让我们说,为了它,不可能在Student对象中添加一个字段(对于更复杂的情况,这是一个玩具问题,我无法修改Student对象)。是否仍然可以更好地创建一个自定义对象,其中学生坐在里面添加另一个字段而不是在比较器中执行complexOperation()?或者还有另一种解决问题的方法吗?我可以考虑创建一个Hashmap,它将student id作为键,complexOperation()的结果作为值,只是在比较器中创建/访问该记录?
答案 0 :(得分:5)
基本上,您希望通过比较每个映射到的某些值来比较学生。这通常由
完成 static Comparator<Student> comparator()
{
return Comparator.comparing( Foo::complexOperation );
}
但是,由于函数complexOperation
过于昂贵,我们希望缓存其结果。我们可以使用通用实用方法Function cache(Function)
static Comparator<Student> comparator()
{
return Comparator.comparing( cache(Foo::complexOperation) );
}
通常,调用者最好提供Map
作为缓存
public static <K,V> Function<K,V> cache(Function<K,V> f, Map<K,V> cache)
{
return k->cache.computeIfAbsent(k, f);
}
我们可以使用IdentityHashMap
作为默认缓存
public static <K,V> Function<K,V> cache(Function<K,V> f)
{
return cache(f, new IdentityHashMap<>());
}
答案 1 :(得分:4)
平均而言,对于N个学生的数组,排序算法将针对log 2 调用complexOperation()
方法N次。如果操作非常慢,那么每个学生最好再运行一次。这可以为1,000名学生提供一个数量级的改进。
但是,您不必明确地执行此操作:您可以使complexOperation(...)
存储每个学生的结果,然后在后续请求中返回缓存值:
private Map<Student,Integer> cache = new HashMap<Student,Integer>();
private int complexOperation(Student s) {
// See if we computed the rank of the student before
Integer res = cache.get(s);
if (res != null) {
// We did! Just return the stored result:
return res.intValue();
}
... // do the real computation here
// Save the result for future invocations
cache.put(s, result);
return result;
}
请注意,要使此方法有效,Student
类需要实现hashCode
和equals
。