Java Collections Framework 实现的 Big-O 总结? [关闭]
Posted
技术标签:
【中文标题】Java Collections Framework 实现的 Big-O 总结? [关闭]【英文标题】:Big-O summary for Java Collections Framework implementations? [closed] 【发布时间】:2010-10-08 06:33:07 【问题描述】:我可能很快就会教授“Java 速成课程”。虽然假设观众成员会知道 Big-O 表示法可能是安全的,但假设他们会知道各种集合实现上的各种操作的顺序可能是不安全的。
我自己可以花时间生成一个汇总矩阵,但如果它已经在公共领域的某个地方出现,我肯定会重复使用它(当然,有适当的信誉。)
有人指点一下吗?
【问题讨论】:
这是一个我发现在讨论一些非常常见的 Java 对象以及使用 Big-O 表示法的操作成本时有用的链接。 objectissues.blogspot.com/2006/11/… 虽然不在公共领域,但 Maurice Naftalin 和 Philip Wadler 的出色 Java Generics and Collections 在其不同集合类的章节中列出了运行时信息概述。 这个performance benchmark 有用吗? 【参考方案1】:Sun 为每个集合类提供的 Javadocs 通常会准确地告诉您您想要什么。 HashMap,例如:
此实现为基本操作(get 和 put)提供恒定时间性能,假设哈希函数将元素正确地分散在桶中。对集合视图的迭代需要时间与 HashMap 实例的“容量”(桶的数量)加上其大小(键值映射的数量)成正比。
TreeMap:
此实现为 containsKey、get、put 和 remove 操作提供保证 log(n) 时间成本。
TreeSet:
此实现为基本操作(添加、删除和包含)提供保证 log(n) 时间成本。
(强调我的)
【讨论】:
我不同意 HashMap 部分。我知道 Sun 的位置,但是... get 例如必须调用 obj.equals(key),这可能与所包含对象的大小成线性关系。考虑到您通常必须阅读此比较的字段。例外是整数或字符串(实习)??? 首先,如果他们错了,创建一个反驳恒定时间性能的测试用例应该不会太难吧?其次,如果您查看 HashMap 的源代码,它不会针对映射中的每个键调用 equals() - 只有当哈希码相等时。 如果您阅读上面的引用,它表示它是恒定时间的“假设哈希函数将元素正确地分散在桶中”。从 CS 理论来看,当哈希函数“好”时(平均发生),哈希表具有恒定的时间操作,但在最坏的情况下可能需要线性时间。 @Overflown - 从技术上讲,从复杂性的角度来看,obj.equals() 花费多长时间并不重要,因为这只是与项目数相关的“常数”的一部分收藏。【参考方案2】:Java Generics and Collections 这本书有此信息(第 188、211、222、240 页)。
列出实现:
get add contains next remove(0) iterator.remove
ArrayList O(1) O(1) O(n) O(1) O(n) O(n)
LinkedList O(n) O(1) O(n) O(1) O(1) O(1)
CopyOnWrite-ArrayList O(1) O(n) O(n) O(1) O(n) O(n)
设置实现:
add contains next notes
HashSet O(1) O(1) O(h/n) h is the table capacity
LinkedHashSet O(1) O(1) O(1)
CopyOnWriteArraySet O(n) O(n) O(1)
EnumSet O(1) O(1) O(1)
TreeSet O(log n) O(log n) O(log n)
ConcurrentSkipListSet O(log n) O(log n) O(1)
地图实现:
get containsKey next Notes
HashMap O(1) O(1) O(h/n) h is the table capacity
LinkedHashMap O(1) O(1) O(1)
IdentityHashMap O(1) O(1) O(h/n) h is the table capacity
EnumMap O(1) O(1) O(1)
TreeMap O(log n) O(log n) O(log n)
ConcurrentHashMap O(1) O(1) O(h/n) h is the table capacity
ConcurrentSkipListMap O(log n) O(log n) O(1)
队列实现:
offer peek poll size
PriorityQueue O(log n) O(1) O(log n) O(1)
ConcurrentLinkedQueue O(1) O(1) O(1) O(n)
ArrayBlockingQueue O(1) O(1) O(1) O(1)
LinkedBlockingQueue O(1) O(1) O(1) O(1)
PriorityBlockingQueue O(log n) O(1) O(log n) O(1)
DelayQueue O(log n) O(1) O(log n) O(1)
LinkedList O(1) O(1) O(1) O(1)
ArrayDeque O(1) O(1) O(1) O(1)
LinkedBlockingDeque O(1) O(1) O(1) O(1)
java.util 包的 javadoc 底部包含一些很好的链接:
Collections Overview 有一个很好的汇总表。 Annotated Outline 在一页上列出了所有实现。【讨论】:
您必须指定这些数字是针对哪种情况,例如,如果您删除数组中间或末尾的元素,则从 Arraylist 中删除可能需要 O(n)。 @popeye 通常不是最坏的情况吗? 正如@Popeye 所提到的,应该有一个关于答案是什么情况的清晰描述。对于时间复杂度,这种情况可以是平均/最差的。看起来答案是指所有 DS 的“平均”案例。【参考方案3】:上面的人对 HashMap / HashSet 与 TreeMap / TreeSet 进行了比较。
我将讨论 ArrayList 与 LinkedList:
数组列表:
O(1)get()
摊销 O(1) add()
如果你使用ListIterator.add()
或Iterator.remove()
在中间插入或删除一个元素,那么后面的所有元素都需要O(n)移位
链表:
O(n)get()
O(1) add()
如果使用ListIterator.add()
或Iterator.remove()
在中间插入或删除一个元素,将会是O(1)
【讨论】:
if you insert or delete an element in the middle using ListIterator.add() or Iterator.remove(), it will be O(1)
为什么?首先我们需要找到中间的元素,那为什么不是 O(n)?
@MyTitle:再读一遍。 “使用ListIterator.add()
或Iterator.remove()
”我们有一个迭代器。【参考方案4】:
这个网站不错,但不是专门针对 Java 的:http://bigocheatsheet.com/
【讨论】:
@AndreaZilio LinkedList.remove(Object) 是常数时间,假设您已经知道邻居。如果你不认识邻居,那么首先找到它是线性时间。 GitHub 中还有一个不错的Runtime Complexity of Java Collections 总结。以上是关于Java Collections Framework 实现的 Big-O 总结? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章