[英]Unexpected complexity of common methods (size) in Java Collections Framework?
最近,我對一些Java集合沒有方法size()的常量時間操作感到驚訝。
雖然我了解到集合的並發實現作為並發增益(在ConcurrentLinkedQueue中的大小為O(n),ConcurrentSkipListSet,LinkedTransferQueue等)的權衡取得了一些妥協,但好消息是這在API文檔中有適當的記錄。
關注我的是某些集合方法返回的視圖的方法大小的性能。 例如, TreeSet.tailSet返回其元素大於或等於fromElement的支持集部分的視圖。 讓我感到驚訝的是,返回的SortedSet上的調用大小在時間上是線性的,即O(n)。 至少這是我設法從OpenJDK的源代碼中挖掘出來的:在TreeSet中實現為TreeMap的包裝器,在TreeMap中,有一個EntrySetView類,其size方法如下:
abstract class EntrySetView extends AbstractSet<Map.Entry<K,V>> {
private transient int size = -1, sizeModCount;
public int size() {
if (fromStart && toEnd)
return m.size();
if (size == -1 || sizeModCount != m.modCount) {
sizeModCount = m.modCount;
size = 0;
Iterator i = iterator();
while (i.hasNext()) {
size++;
i.next();
}
}
return size;
}
....
}
這意味着第一次調用大小是O(n),然后只要不修改后備映射就會緩存它。 我無法在API文檔中找到這個事實。 更有效的實現將是O(log n),其中在子樹大小的緩存中具有存儲器權衡。 由於這種權衡是為了避免代碼重復(TreeSet作為TreeMap的包裝器),我沒有看到為什么不應該出於性能原因而制作它們的原因。
無視我對TreeSet的OpenJDK實現的(非常簡短的)分析是對還是錯,我想知道是否有關於許多此類操作的性能的詳細而完整的文檔,尤其是那些完全出乎意料的操作?
例如,
TreeSet.tailSet
返回其元素大於或等於fromElement
的支持集部分的視圖。 讓我感到驚訝的是,返回的SortedSet
上的調用size
在時間上是線性的,即O(n)
。
對我來說這並不奇怪。 考慮來自javadoc的這句話:
“返回的集合由此集合支持,因此返回集合中的更改將反映在此集合中,反之亦然。”
由於尾部集是背襯集的動態視圖,因此必須在實踐中動態計算其大小。 替代方案將要求在對支持集進行更改時,必須調整所有現存的尾部(和耳機)視圖的大小。 這將使支持集的更新更加昂貴,並且會出現存儲管理問題。 (為了更新視圖大小,后台集需要引用所有現有視圖集......這可能是隱藏的內存泄漏。)
現在你對文檔有一點看法。 但實際上,javadocs沒有說明視圖集的復雜性。 事實上,它甚至沒有記錄TreeSet.size()
是O(1)
! 實際上,它只記錄了add
, remove
和contains
操作的復雜性。
我想知道是否有關於許多此類操作的性能的詳細而完整的文檔,尤其是那些完全出乎意料的操作?
AFAIK,不。當然,不是來自Sun / Oracle ......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.