使用自定义比较器时使用 TreeSet 还是 ArrayList 更好

Posted

技术标签:

【中文标题】使用自定义比较器时使用 TreeSet 还是 ArrayList 更好【英文标题】:Is it better to use a TreeSet or ArrayList when using a custom comparator 【发布时间】:2014-08-13 19:19:08 【问题描述】:

我已经实现了一个图表。 我想根据度数对给定的顶点子集进行排序。 因此,我编写了一个名为DegreeComparator 的自定义比较器。

private class DegreeComparator implements Comparator<Integer>

    @Override
    public int compare(Integer arg0, Integer arg1) 
    
        if(adj[arg1].size() == adj[arg0].size()) return arg1 - arg0;
        else return adj[arg1].size() - adj[arg0].size());
    


那么,以下哪一种更有效?

使用TreeSet

public Collection<Integer> sort(Collection<Integer> unsorted)

    Set<Integer> sorted = new TreeSet<Integer>(new DegreeComparator());
    sorted.addAll(unsorted);
    return sorted;

使用ArrayList

Collections.sort(unsorted, new DegreeComparator());

注意第二种方法不是函数,而是一行代码。

直觉上,我宁愿选择第二个。但我不确定它是否更有效。

【问题讨论】:

如果您担心速度,请避免将ints 装进Integers。 在固定输入大小的情况下占用更少的时间和空间。时间先于空间。 【参考方案1】:

Java API 包含许多 Collection 和 Map 实现,因此可能会混淆使用哪一个。这是一个快速流程图,可能有助于从最常见的实现中进行选择

【讨论】:

我错过了LinkedListQueues/Deques、Concurrent*Blocking**SkipLists 此图表无法用于您想要排序的任何内容,例如显示(例如,它与搜索无关)。它实际上也没有回答最初的问题,即关于性能,而不是选择哪种类型的集合。【参考方案2】:

TreeSet 是一个集合。它删除重复项(具有相同程度的元素)。所以两者不等价。

无论如何,如果您自然想要一个排序列表,那么对列表进行排序。无论集合是否有重复,这都会起作用,即使它具有与填充 TreeSet 相同的复杂度 (O(n*log(n)),它也可能更快(因为它只需要移动数组中的元素,而不必创建大量的树节点)。

【讨论】:

所以,我应该稍微修改一下比较器。请查看我的修改以了解我的修改。 @OnurÇağırıcı TreeSet 仍会删除重复项,我认为这是设计使然?? @PeterLawrey 新修改如何删除重复项?我没有两个具有相同 ID 的顶点。如果它们的连通性相同,它将返回 ID 之间的差异。 @OnurÇağırıcı 你没有说你不会有重复的值。如果您没有重复项,则无需删除重复项。在 ArrayList 中,即使您有重复项,它也不会删除它们。 ArrayList 更快,所以这可能对你有利。 在 OP 描述的情况下它们是等效的(在您回答后 20 分钟)。【参考方案3】:

如果您只排序一次,那么ArrayList 显然是赢家。如果您经常添加或删除项目,TreeSet 会更好,因为一次又一次地对列表进行排序会很慢。

另请注意,所有树结构都需要更多内存和内存访问间接,这会使它们变慢。


如果是中等大小的列表,由于单个元素的变化相当频繁,最快的解决方案可能是使用 ArrayList 并插入到正确的位置(显然假设数组最初是排序的)。

您需要通过Arrays.binarySearch 确定插入位置并插入或删除。实际上,我不会这样做,除非性能非常关键并且基准测试会显示它有帮助。当列表变得非常大并且由于 Java 使用 TimSort(针对这种情况进行了优化)而增益有限时,它会变得很慢。


正如评论中所指出的,确保Comparator 返回不同的值有时并非易事。好在有Guava的Ordering#arbitrary,如果不需要兼容equals就解决了。如果你这样做了,可以编写一个类似的方法(如果需要,我相信我可以在某个地方找到它)。

【讨论】:

列表是动态的。实际上,有两个不相交的列表,我正在向一个列表添加一个顶点,同时从另一个列表中删除它。 @OnurÇağırıcı 如果您经常这样做,这会使您充满活力。如果列表少于 100 个元素,并且您每秒执行的次数不超过 100 次,则无需关心。你是root of all evil? 很遗憾,不,我的列表有 1000 多个元素,我不知道我做了多少次,但显然超过了 100 次。 @OnurÇağırıcı 然后可能对其进行优化。与否,我建议您在开始过多优化之前对其进行基准测试。我会在大约一小时后发布另一个想法。

以上是关于使用自定义比较器时使用 TreeSet 还是 ArrayList 更好的主要内容,如果未能解决你的问题,请参考以下文章

TreeSet

Collection接口下的Set接口TreeSet类中的自定义比较策略

TreeSet

Comparator与Comparable,自定义排序和类比较器,TreeSet对象排序

TreeSet

面试题: TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常!