Java 是添加到集合然后对其排序更快,还是添加到已排序的集合更快?
如果我有一张这样的Java 是添加到集合然后对其排序更快,还是添加到已排序的集合更快?,java,sorting,collections,Java,Sorting,Collections,如果我有一张这样的地图: HashMap<Integer, ComparableObject> map; (B) 创建有序集合的实例,如TreeSet,然后添加值: Set<ComparableObject> sortedCollection = new TreeSet<ComparableObject>(map.values()); Set sortedCollection=new TreeSet(map.values()); 请注意,生成的集合永远不
地图
:
HashMap<Integer, ComparableObject> map;
(B)
创建有序集合的实例,如TreeSet
,然后添加值:
Set<ComparableObject> sortedCollection = new TreeSet<ComparableObject>(map.values());
Set sortedCollection=new TreeSet(map.values());
请注意,生成的集合永远不会被修改,因此排序只需要进行一次。理论上,最后的排序应该更快。 在整个进程中维护排序状态可能需要额外的CPU时间 从CS的角度来看,这两个操作都是NlogN,但1个排序应该具有较低的常数。TreeSet具有
log(n)
时间复杂性保证,用于add()/remove()/contains()
方法。
排序ArrayList
需要n*log(n)
操作,但是add()/get()
只需要1
操作
因此,如果您主要是检索,而不经常排序,
ArrayList
是更好的选择。如果您经常排序,但没有检索到那么多TreeSet
将是一个更好的选择。如果您选择实现B),请务必阅读我在底部对TreeSet的评论
如果你的应用程序只是偶尔进行排序,但经常重复,我认为你最好使用一个简单的未排序列表。对它进行一次排序,然后从更快的迭代中获益。迭代在数组列表上特别快
但是,如果您希望始终保证排序顺序,或者您可能经常添加/删除元素,那么就使用排序集合,并在迭代中获得成功
所以在你的情况下,我会说A)是更好的选择。该列表只排序一次,不会更改,因此作为一个数组很有好处。迭代应该非常快,特别是如果您知道它是ArrayList,并且可以直接使用ArrayList.get()而不是迭代器
我还要补充一点,树集的定义是一个集合,这意味着对象是唯一的。树集通过在Comparator/Comparable上使用compareTo来确定相等性。如果尝试添加两个compareTo返回值为0的对象,则很容易发现自己缺少数据。e、 g.在树集合中添加“C”、“A”、“B”、“A”将返回“A”、“B”、“C”为什么不同时使用这两个世界中的最好的一个呢?若不再使用它,请使用树集进行排序,并使用内容初始化ArrayList
List<ComparableObject> sortedCollection =
new ArrayList<ComparableObject>(
new TreeSet<ComparableObject>(map.values()));
奇怪的是,我的方法在迭代中表现最好(我原以为迭代中与ArrayList方法没有区别,我的基准测试中有bug吗?)
免责声明:我知道这可能是一个糟糕的基准,但它有助于让您理解这一点,我当然没有操纵它以使我的方法获胜
(对于equals/hashcode/compareTo构建器,代码依赖于apache commons/lang,但重构起来应该很容易)
集合。sort
使用带有O(nlog n)的mergeSort
TreeSet
具有红黑树底层,基本操作具有O(logn)。因此n个元素也有O(nlogn)
因此,这两种算法都是相同的大O算法。在一个分类数据集中插入的是O(log(n))(但是!是当前的n,而不是最终的n)。 在列表中插入是1 插入中已包含SortedSet中的排序,因此为0。 列表中的排序是O(n*log(n)) 因此,除最后一种情况外,所有情况下的SortedSet总复杂度都是O(n*k),k
- 使用分类数据集进行插入
- 将SortedSet作为创建要返回的列表的参数
- 例如,如果要排序的集合是短期的,用作方法的参数,并且需要在方法中对列表进行排序,则使用Collections.sort(Collection)。或者,如果它是长寿命对象,但您需要很少对其进行排序 理由:排序后的集合是特定内容所必需的,您可能不会经常添加或删除。因此,在对集合进行排序后,您并不真正关心集合中的元素。你基本上: 排序->使用它->忘记 如果向已排序的集合中添加新元素,则必须再次对集合进行排序,因为插入新元素时不能保证顺序
- 如果要排序的集合是长期存在的,并且/或者如果它是类中的一个字段,并且您需要始终对其进行排序,那么您应该使用排序数据结构,例如TreeSet 理由:您始终关心收款顺序。您希望随时对其进行排序。因此,如果不断添加或删除元素,则可以保证集合已排序。所以基本上: 插入/删除->使用它(只要您保证集合已排序) 没有特定的时刻需要对集合进行排序,相反,您希望始终对集合进行排序 使用TreeSet的缺点是它需要保留已排序集合的资源。它使用红黑树,并且get、put操作需要O(logn)时间开销
- 好问题,好答案。我只是想补充几点考虑:
而如果使用简单的集合,例如ArrayList,则get、add操作是O(1)恒定时间。这取决于输入数据的顺序-例如,如果您提取了很多行并使用了顺序,那么这是一种情况-如果您有一组随机的GUID-另一种情况。为什么不使用树形图呢?树形图在这里没有帮助,因为需要对值(
ComparableObject
List<ComparableObject> sortedCollection = new ArrayList<ComparableObject>( new TreeSet<ComparableObject>(map.values()));
compareTo() calls:123490
Transformer ArrayListTransformer
Creation: 255885873 ns (0.255885873 seconds)
Iteration: 2582591 ns (0.002582591 seconds)
Item count: 10000
compareTo() calls:121665
Transformer TreeSetTransformer
Creation: 199893004 ns (0.199893004 seconds)
Iteration: 4848242 ns (0.004848242 seconds)
Item count: 10000
compareTo() calls:121665
Transformer BestOfBothWorldsTransformer
Creation: 216952504 ns (0.216952504 seconds)
Iteration: 1604604 ns (0.001604604 seconds)
Item count: 10000
compareTo() calls:18819
Transformer PriorityQueueTransformer
Creation: 35119198 ns (0.035119198 seconds)
Iteration: 2803639 ns (0.002803639 seconds)
Item count: 10000