Niedawno byłem zaskoczony faktem, że niektóre kolekcje Java nie mają stałej operacji czasowej metody size().Nieoczekiwana złożoność typowych metod (rozmiaru) w Java Collections Framework?
Gdy dowiedziałem się, że równoczesne implementacje kolekcji wykonane pewne kompromisy jako kompromis dla przyrostu współbieżności (rozmiar jest O (n) w ConcurrentLinkedQueue, ConcurrentSkipListSet, LinkedTransferQueue, etc.) Dobrą wiadomością jest to, że są właściwie udokumentowane w dokumentacji API .
Co mnie niepokoi, to wydajność rozmiaru metody na widokach zwróconych przez niektóre metody zbiorów. Na przykład TreeSet.tailSet zwraca widok części zestawu kopii, którego elementy są większe lub równe odElement. Bardzo mnie zaskoczyło to, że rozmiar wywołania przy zwrocie SortedSet jest liniowy w czasie, czyli O (n). Przynajmniej to, co udało mi się wygrzebać z kodu źródłowego OpenJDK: W TreeSet jest zaimplementowany jako owinięcia nad TreeMap, aw ciągu TreeMap istnieje klasa EntrySetView którego metoda wielkość jest w następujący sposób:
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;
}
....
}
Oznacza to, że pierwszy rozmiar jest wywoływany to O (n), a następnie jest buforowany, o ile mapa podkładu nie jest modyfikowana. Nie mogłem znaleźć tego faktu w dokumentacji API. Bardziej wydajną implementacją byłby O (log n) z kompromisem pamięci w buforowaniu wielkości poddrzewa. Ponieważ takie kompromisy są podejmowane w celu uniknięcia powielania kodu (TreeSet jako wrapper na TreeMap), nie widzę powodu, dla którego nie powinny one być tworzone ze względu na wydajność.
Pomijając moje bycie dobrym lub złym z moją (bardzo krótką) analizą wdrożenia TreeSet w OpenJDK, chciałbym wiedzieć, czy istnieje szczegółowa i kompletna dokumentacja dotycząca wykonywania wielu takich operacji, w szczególności tych, które są całkowicie nieoczekiwane?
Chciałbym myśleć, że Javadocs zawierać te informacje. – Thilo
Interesujące pytanie (+1). Nie mogę pomóc w sprawie dokumentacji (nie widziałem złożoności jawnie udokumentowanej). Jednak osobiście uważam zachowanie 'tailSet() za całkowicie intuicyjne. Sądzę, że bardziej zaskakujące byłoby oczekiwanie od każdego, kto zapłaci za karę za pamięć, aby marginalna szansa na użycie miała lepszą wydajność. – NPE
@NPE Czy zgadzasz się z karą za utratę pamięci, którą wszyscy mamy za to, że każdy zestaw został zaimplementowany jako opakowanie dla mapy tylko dla deweloperów JDK, którzy nie muszą dwa razy wykonywać tych samych funkcji? :) Myślę, że dość jasno stwierdziłem, że mój problem dotyczy samej dokumentacji, a nie samej wydajności. Co mnie myli to to, że TreeMap.size to O (1), TreeMap.tailSet to O (log N) i nie ma informacji dla TreeMap.tailSet(). Size() i wiem, że może to być O (log n) i Na). – mario