使用自定义比较器时使用 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());
注意第二种方法不是函数,而是一行代码。
直觉上,我宁愿选择第二个。但我不确定它是否更有效。
【问题讨论】:
如果您担心速度,请避免将int
s 装进Integer
s。
在固定输入大小的情况下占用更少的时间和空间。时间先于空间。
【参考方案1】:
Java API 包含许多 Collection 和 Map 实现,因此可能会混淆使用哪一个。这是一个快速流程图,可能有助于从最常见的实现中进行选择
【讨论】:
我错过了LinkedList
、Queue
s/Deque
s、Concurrent*
、Blocking*
和 *SkipList
s
此图表无法用于您想要排序的任何内容,例如显示(例如,它与搜索无关)。它实际上也没有回答最初的问题,即关于性能,而不是选择哪种类型的集合。【参考方案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 更好的主要内容,如果未能解决你的问题,请参考以下文章
Collection接口下的Set接口TreeSet类中的自定义比较策略
Comparator与Comparable,自定义排序和类比较器,TreeSet对象排序
面试题: TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常!