Java 比较器工作方式的效率

Java 比较器工作方式的效率,java,performance,sorting,comparator,Java,Performance,Sorting,Comparator,我正在尝试使用比较器来帮助对对象列表进行排序。我有一个问题,关于比较器到底是如何工作的,以及在下面的示例中它到底在做什么: private static Comparator<Student> comparator() { return (Student a, Student b) -> { return Integer.compare(complexOperation(a), complexOperation

我正在尝试使用比较器来帮助对对象列表进行排序。我有一个问题,关于比较器到底是如何工作的,以及在下面的示例中它到底在做什么:

private static Comparator<Student> comparator()
{
        return (Student a, Student b) ->
        {  
                return Integer.compare(complexOperation(a), complexOperation(b));
        }
}
这两种方法是否具有可比性?或者,由于比较器的工作方式(可能会将同一对象与其他对象进行多次比较,从而在比较过程中每个学生多次运行complexOperation()),在学生字段中预计算complexOperation()结果会更快吗

上述情况可以这样称呼:

Collections.sort(students, comparator());
希望这是清楚的

编辑:
比方说,为了方便起见,不可能向学生对象添加字段(对于更复杂的情况,这是一个玩具问题,因为我无权修改学生对象)。也许创建一个自定义对象,让Student坐在其中并添加另一个字段,而不是在comparator中执行complexOperation()会更好吗?还是有其他办法解决这个问题?我可以考虑创建一个Hashmap,该Hashmap以student id为键,complexOperation()的结果为值,只在comparator中创建/访问该记录?

平均而言,排序算法将为N个student数组调用
complexOperation()
方法大约log2N次。如果操作真的很慢,你最好为每个学生运行一次。这将为1000名学生带来一个数量级的进步

但是,您不必显式地执行此操作:您可以使
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;
}
private Map cache=new HashMap();
专用int complexOperation(学生s){
//看看我们以前是否计算过这个学生的排名
整数res=cache.get(s);
如果(res!=null){
//我们做到了!只需返回存储的结果:
返回res.intValue();
}
…//在这里进行真正的计算
//保存结果以备将来调用
cache.put(s,result);
返回结果;
}

请注意,为了使这种方法起作用,
Student
类需要实现
hashCode
equals
,基本上,您希望通过比较每个映射到的一些值来比较学生。这通常是由

    static Comparator<Student> comparator()
    {
        return Comparator.comparing( 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);
}

@HoverCraftfullOfels我特别使用比较器作为排序机制,希望它能尽可能地发挥性能。
(可能会将同一对象与其他对象进行多次比较,因此在比较过程中每个学生会多次运行complexOperation()
-添加System.out.println(…)语句,查看调用的频率。或者添加某种计数器,可以在比较器完成后显示。如果调用的数量大于正在排序的元素,则在多次调用时,您知道复杂的操作。显示某些输出的基本问题解决技术。然后,您将询问abJVM的优化工作,如果它确定这将使事情更高效地运行,它通常会为您做这类事情。@bayou.io缓存的清除需要显式地完成,或者通过丢弃拥有缓存的对象来完成。
comparator()的用户
method可能不想被这个细节所困扰:)@JohnBaum是的,最好用一个额外的
int
字段来创建一个“holder”,特别是对于大组学生,调用次数增加了十倍或更多。与潜在的CPU节省相比,对象开销代表了一个微小的成本。@约翰鲍姆这与我上面建议的差不多,除了您的方法使用student ID作为键,而我直接使用
student
,而不提取其ID(不过,引擎盖下的
equal
hashCode
很可能依赖于ID)。除此之外,这两种方法是相同的。仅供参考,这都是理论上的,是的,缓存将提高性能,代价是使代码复杂化,因此,除非您确实存在性能问题,否则不要麻烦。这是一句古老的格言,即在出现问题之前不调整代码,因为您可能会在错误的程序上浪费时间问题是,如果你有时间的话,你就没有时间去发现和解决真正的问题了。我想知道为什么Java的
比较器。比较
在默认情况下(或者至少是可选的)。在Python中,
sorted
with
key
函数会这样做。
    static Comparator<Student> comparator()
    {
        return Comparator.comparing( cache(Foo::complexOperation) );
    }
public static <K,V> Function<K,V> cache(Function<K,V> f, Map<K,V> cache)
{
    return k->cache.computeIfAbsent(k, f);
}
public static <K,V> Function<K,V> cache(Function<K,V> f)
{
    return cache(f, new IdentityHashMap<>());
}