Zawsze byłam osobą, która po prostu używa:
List<String> names = new ArrayList<>();
Używam interfejsu jako nazwy typu dla przenośności , więc kiedy zadaję takie pytania, mogę przerobić mój kod.
Kiedy należy LinkedList
stosować na ArrayList
odwrót?
java
arraylist
collections
linked-list
sdellysse
źródło
źródło
Odpowiedzi:
Podsumowanie
ArrayList
zArrayDeque
jest lepsze w znacznie większej liczbie przypadków użycia niżLinkedList
. Jeśli nie jesteś pewien - zacznij odArrayList
.LinkedList
iArrayList
są dwiema różnymi implementacjami interfejsu List.LinkedList
implementuje go z podwójnie połączoną listą.ArrayList
implementuje go za pomocą dynamicznie zmieniającej się tablicy.Podobnie jak w przypadku standardowych połączonych operacji na liście i tablicach, różne metody będą miały różne środowiska uruchomieniowe algorytmu.
Dla
LinkedList<E>
get(int index)
to O (n) ( średnio n / 4 kroki), ale O (1), gdyindex = 0
lubindex = list.size() - 1
(w tym przypadku możesz także użyćgetFirst()
igetLast()
). Jedną z głównych zaletLinkedList<E>
add(int index, E element)
to O (n) ( średnio z n / 4 krokami), ale O (1), gdyindex = 0
lubindex = list.size() - 1
(w tym przypadku można również użyćaddFirst()
iaddLast()
/add()
). Jedną z głównych zaletLinkedList<E>
remove(int index)
to O (n) ( średnio n / 4 kroki), ale O (1), gdyindex = 0
lubindex = list.size() - 1
(w tym przypadku możesz także użyćremoveFirst()
iremoveLast()
). Jedną z głównych zaletLinkedList<E>
Iterator.remove()
oznacza O (1) . Jedną z głównych zaletLinkedList<E>
ListIterator.add(E element)
oznacza O (1) . Jedną z głównych zaletLinkedList<E>
Uwaga: wiele operacji wymaga n / 4 średnio kroków, stałej liczby kroków w najlepszym przypadku (np. Indeks = 0) i n / 2 kroków w najgorszym przypadku (środek listy)
Dla
ArrayList<E>
get(int index)
to O (1) . Główną zaletąArrayList<E>
add(E element)
to O (1) amortyzowane przez , ale w najgorszym przypadku O (n), ponieważ rozmiar tablicy musi zostać zmieniony i skopiowanyadd(int index, E element)
to O (n) ( średnio n / 2 kroki)remove(int index)
to O (n) ( średnio n / 2 kroki)Iterator.remove()
to O (n) ( średnio n / 2 kroki)ListIterator.add(E element)
oznacza O (n) (zśrednio n / 2 kroki)Uwaga: wiele operacji wymaga średnio n / 2 kroków, stałych liczby kroków w najlepszym przypadku (koniec listy), n kroków w najgorszym przypadku (początek listy)
LinkedList<E>
pozwala na wstawianie lub usuwanie w stałym czasie za pomocą iteratorów , ale tylko sekwencyjny dostęp do elementów. Innymi słowy, możesz przejść listę do przodu lub do tyłu, ale znalezienie pozycji na liście zajmuje czas proporcjonalny do wielkości listy. Javadoc mówi: „operacje indeksujące listę będą przechodzić przez listę od początku lub końca, w zależności od tego, co jest bliższe” , więc te metody to średnio O (n) ( n / 4 kroki), chociaż O (1) dlaindex = 0
.ArrayList<E>
, z drugiej strony, umożliwia szybki losowy dostęp do odczytu, dzięki czemu można pobrać dowolny element w stałym czasie. Ale dodawanie lub usuwanie z dowolnego miejsca poza końcem wymaga przesunięcia wszystkich ostatnich elementów, aby zrobić otwór lub wypełnić lukę. Ponadto, jeśli dodasz więcej elementów niż pojemność tablicy bazowej, przydzielana jest nowa tablica (1,5 razy większa), a stara tablica jest kopiowana do nowej, więc dodawanie doArrayList
jest O (n) w najgorszym przypadek, ale średnio stały.Dlatego w zależności od operacji, które zamierzasz wykonać, powinieneś odpowiednio wybrać implementacje. Iterowanie po każdym rodzaju listy jest praktycznie równie tanie. (Iteracja
ArrayList
jest technicznie szybsza, ale jeśli nie robisz czegoś naprawdę wrażliwego na wydajność, nie powinieneś się tym przejmować - oba są stałe.)Główne zalety używania
LinkedList
powstają, gdy ponownie wykorzystujesz istniejące iteratory do wstawiania i usuwania elementów. Operacje te można następnie wykonać w O (1) , zmieniając listę tylko lokalnie. Na liście tablic pozostałą część tablicy należy przenieść (tzn. Skopiować). Z drugiej strony, szukanie wLinkedList
sposób podążający za linkami w O (n) ( n / 2 krokach) w najgorszym przypadku, podczas gdy wArrayList
pożądanej pozycji można obliczyć matematycznie i uzyskać dostęp w O (1) .Kolejna korzyść z używania
LinkedList
pojawia się, gdy dodajesz lub usuwasz z nagłówka listy, ponieważ te operacje to O (1) , podczas gdy są one O (n) dlaArrayList
. Pamiętaj, żeArrayDeque
może to być dobra alternatywa dlaLinkedList
dla dodawania i usuwania z głowy, ale nie jest toList
.Ponadto, jeśli masz duże listy, pamiętaj, że użycie pamięci jest również inne. Każdy element a
LinkedList
ma większy narzut, ponieważ przechowywane są również wskaźniki do następnego i poprzednich elementów.ArrayLists
nie mam tego narzutu. Jednak,ArrayLists
zajmij tyle pamięci, ile jest przydzielone dla pojemności, niezależnie od tego, czy elementy zostały rzeczywiście dodane.Domyślna początkowa pojemność
ArrayList
jest dość mała (10 z Java 1.4 - 1.8). Ponieważ jednak podstawową implementacją jest tablica, należy zmienić jej rozmiar, jeśli dodasz wiele elementów. Aby uniknąć wysokich kosztów zmiany rozmiaru, gdy wiesz, że zamierzasz dodać wiele elementów, stwórzArrayList
większą pojemność początkową.źródło
O(n/2)
lubO(n/4)
. Duża notacja O mówi, jak skaluje się operacja z większym n . a operacja wymagającan/2
kroków jest skalowana dokładnie tak, jak operacja wymagającan
kroków, co jest powodem, dla którego usuwane są stałe sumy lub czynniki.O(n/2)
iO(n/4)
oba są sprawiedliweO(n)
.LinkedList
i takArrayList
czy inaczej mają różne stałe czynniki, więc nie byłoby sensu porównywaćO(n/2)
jednego zO(n/4)
drugim, oba oznaczają po prostu operacje skalowania liniowego.Jak dotąd wydaje się, że nikt nie zajął się śladami pamięci każdej z tych list oprócz ogólnego konsensusu, że a
LinkedList
jest „o wiele więcej” niżArrayList
tak, więc zrobiłem pewne chrupanie liczb, aby pokazać dokładnie, ile obie listy zajmują dla zerowych referencji.Ponieważ referencje mają 32 lub 64 bity (nawet gdy są zerowe) w ich systemach względnych, dołączyłem 4 zestawy danych dla 32 i 64 bitów
LinkedLists
iArrayLists
.Uwaga: Rozmiary pokazane dla
ArrayList
linii są dla przyciętych list - W praktyce pojemność tablicy podkładowej wArrayList
jest ogólnie większa niż bieżąca liczba elementów.Uwaga 2: (dzięki BeeOnRope) Ponieważ CompressedOops jest teraz domyślny od połowy JDK6 i wyższych, poniższe wartości dla komputerów 64-bitowych będą w zasadzie pasowały do ich 32-bitowych odpowiedników, chyba że specjalnie je wyłączysz.
Wynik wyraźnie pokazuje, że
LinkedList
jest to o wiele więcej niżArrayList
, szczególnie przy bardzo dużej liczbie elementów. Jeśli ważna jest pamięć, omijaj jąLinkedLists
.Formuły, których użyłem, są następujące. Daj mi znać, jeśli zrobiłem coś złego, i naprawię to. „b” wynosi 4 lub 8 dla systemów 32- lub 64-bitowych, a „n” oznacza liczbę elementów. Zauważ, że powodem modyfikacji jest to, że wszystkie obiekty w Javie zajmą wielokrotność 8-bajtowej przestrzeni, niezależnie od tego, czy wszystko jest używane, czy nie.
ArrayList:
ArrayList object header + size integer + modCount integer + array reference + (array oject header + b * n) + MOD(array oject, 8) + MOD(ArrayList object, 8) == 8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8) + MOD(8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8), 8)
Połączona lista:
LinkedList object header + size integer + modCount integer + reference to header + reference to footer + (node object overhead + reference to previous element + reference to next element + reference to element) * n) + MOD(node object, 8) * n + MOD(LinkedList object, 8) == 8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n + MOD(8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n, 8)
źródło
int
4 lub 8 bajtów danych. Na połączonej liście znajdują się zasadniczo 4 „słowa” narzutu. Twój wykres sprawia więc wrażenie, że połączone listy używają „pięciokrotnie” miejsca do przechowywania list tablic. To jest źle. Narzut wynosi 16 lub 32 bajty na obiekt, co stanowi korektę addytywną, a nie współczynnik skalowania.CompressedOops
jest teraz domyślny we wszystkich najnowszych pakietach JDK (7, 8 i aktualizacjach 6 przez kilka lat), więc 64-bit nie będzie różnił sięArrayList
aniLinkedList
rozmiarami, chyba że jawnie wyłączyłeś skompresowane ups dla jakiś powód.ArrayList
bez określenia początkowej pojemności nadal będzie zużywał znacznie mniej pamięci niż aLinkedList
.ArrayList
jest tym, czego chcesz.LinkedList
jest prawie zawsze błędem (wydajnościowym).Dlaczego
LinkedList
jest do bani:ArrayList
użycia.ArrayList
, prawdopodobnie i tak będzie znacznie wolniejsza.LinkedList
w źródle niepokojące, ponieważ prawdopodobnie jest to zły wybór.źródło
Jako ktoś, kto zajmuje się inżynierią wydajności operacyjnej w bardzo dużych serwisach internetowych SOA od około dekady, wolałbym zachowanie LinkedList nad ArrayList. Podczas gdy przepustowość w stanie ustalonym LinkedList jest gorsza, a zatem może prowadzić do zakupu większej ilości sprzętu - zachowanie ArrayList pod presją może prowadzić do rozszerzenia aplikacji w klastrze prawie synchronicznie, a dla dużych rozmiarów macierzy może prowadzić do braku reakcji w aplikacji i przestoju, będąc pod presją, co jest katastrofalnym zachowaniem.
Podobnie, możesz uzyskać lepszą przepustowość w aplikacji z domyślnego dzierżawionego śmietnika, ale gdy otrzymasz aplikacje Java z 10 GB hałdami, możesz skończyć blokowanie aplikacji na 25 sekund podczas pełnych GC, co powoduje przekroczenie limitu czasu i awarie w aplikacjach SOA i zdmuchuje twoje umowy SLA, jeśli występują zbyt często. Chociaż moduł zbierający CMS zajmuje więcej zasobów i nie osiąga tej samej surowej przepustowości, jest o wiele lepszym wyborem, ponieważ ma bardziej przewidywalne i mniejsze opóźnienia.
ArrayList jest lepszym wyborem dla wydajności, jeśli chodzi o przepustowość i można zignorować opóźnienia. Z mojego doświadczenia w pracy nie mogę zignorować opóźnień w najgorszym przypadku.
źródło
LinkedList
zawsze przydziela pięciokrotnie pamięć niż zwykła tablica referencji, więcArrayList
czasowe wymaganie 2,5 razy nadal zużywa znacznie mniej pamięci, nawet jeśli pamięć nie jest odzyskana. Ponieważ przydział dużych tablic omija przestrzeń Eden, nie mają one żadnego wpływu na zachowanie GC, chyba że naprawdę nie ma wystarczającej ilości pamięci, w którym to przypadkuLinkedList
wybuchło znacznie wcześniej…LinkedList
potrzebuje tylko niewielkiej ilości wolnej pamięci, aby przydzielić na następny element.ArrayList
będzie potrzebował dużego i ciągłego wolnego bloku miejsca, aby przydzielić zmienioną tablicę. Jeśli sterty ulegną fragmentacji, GC może skończyć z kolejnością całej sterty, aby zwolnić odpowiedni pojedynczy blok pamięci.Algorytmy: Notacja Big-Oh
ArrayLists są dobre dla wielu-dodających-do-odczytu-wielu lub dołączających, ale złe przy dodawaniu / usuwaniu z przodu lub ze środka.
źródło
O(1)
. Musi przebiegać przez połowę listy, aby znaleźć punkt wstawiania.LinkedList
jest,O(1)
jeśli masz iterator do pozycji wstawiania , tzn.ListIterator.add
jest podobnoO(1)
dlaLinkedList
.Tak, wiem, to starożytne pytanie, ale wrzucę moje dwa centy:
LinkedList jest prawie zawsze złym wyborem pod względem wydajności. Istnieje kilka bardzo specyficznych algorytmów, do których wzywa się LinkedList, ale są one bardzo, bardzo rzadkie, a algorytm zwykle zależy w szczególności od zdolności LinkedList do stosunkowo szybkiego wstawiania i usuwania elementów na środku listy, gdy już tam przejdziesz z ListIteratorem.
Istnieje jeden typowy przypadek użycia, w którym LinkedList przewyższa ArrayList: kolejka. Jeśli jednak Twoim celem jest wydajność, zamiast LinkedList powinieneś również rozważyć użycie ArrayBlockingQueue (jeśli możesz wcześniej ustalić górną granicę wielkości kolejki i stać Cię na przydzielenie całej pamięci z góry), lub tę implementację CircularArrayList . (Tak, pochodzi z 2001 roku, więc musisz go wygenerować, ale otrzymałem porównywalne wskaźniki wydajności do tego, co jest cytowane w tym artykule w ostatnim JVM)
źródło
ArrayDeque
. docs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.htmlArrayDeque
jest wolniejszy niż,LinkedList
chyba że wszystkie operacje są na tym samym końcu. Jest OK, gdy jest używany jako stos, ale nie tworzy dobrej kolejki.ArrayDeque
prawdopodobnie będzie szybszy niż wStack
przypadku użycia go jako stosu i szybciej niż wLinkedList
przypadku użycia go jako kolejki.ArrayDeque
dokumentacji.To pytanie o efektywność.
LinkedList
jest szybki do dodawania i usuwania elementów, ale powolny dostęp do określonego elementu.ArrayList
jest szybki do uzyskiwania dostępu do określonego elementu, ale może być powolny, aby dodać na każdym końcu, a szczególnie wolno, aby usunąć na środku.Array vs ArrayList vs LinkedList vs Vector idzie głębiej, podobnie jak lista połączona .
źródło
Prawidłowe lub niepoprawne: proszę wykonać test lokalnie i sam zdecydować!
Edycja / Usuń jest szybsza
LinkedList
niżArrayList
.ArrayList
, wspierany przezArray
, który musi być dwukrotnie większy, jest gorszy w aplikacjach o dużej objętości.Poniżej znajduje się wynik testu jednostkowego dla każdej operacji. Czas trwania podano w nanosekundach.
Oto kod:
źródło
LinkedList
ma o wiele więcej pamięci, ponieważ dla każdego elementu istnieje obiekt węzła z pięcioma polami. W wielu systemach powoduje to obciążenie 20 bajtów. Średni narzut pamięci przypadający na elementArrayList
wynosi półtora słowa, co daje 6 bajtów i 8 bajtów w najgorszym przypadku.removeIf(element -> condition)
gdzie pasuje, co może być znacznie szybszeArrayList
, w porównaniu do zapętlania i usuwania za pomocą iteratora, ponieważ nie jest wymagane przesunięcie całej reszty dla każdego elementu. To, czy działa to lepiej, czy gorzej niżLinkedList
zależy od konkretnego scenariusza, ponieważ aLinkedList
jest teoretycznie O (1), ale usunięcie tylko jednego węzła wymaga kilku dostępów do pamięci, które mogą łatwo przekroczyć liczbę potrzebną doArrayList
usunięcia znacznej liczby elementów .ArrayList
jest w zasadzie tablicą.LinkedList
jest zaimplementowany jako podwójnie połączona lista.To
get
jest całkiem jasne. O (1) dlaArrayList
, ponieważArrayList
pozwalają na losowy dostęp przy użyciu indeksu. O (n) dlaLinkedList
, ponieważ najpierw musi znaleźć indeks. Uwaga: istnieją różne wersjeadd
iremove
.LinkedList
jest szybsze w dodawaniu i usuwaniu, ale wolniejsze. W skrócie,LinkedList
należy preferować, jeśli:=== ArrayList ===
=== LinkedList ===
dodaj (E e)
add (int index, element E)
Oto rysunek z programcreek.com (
add
iremove
są pierwszym typem, tj. Dodaj element na końcu listy i usuń element w określonej pozycji na liście.):źródło
Joshua Bloch, autor LinkedList:
Link: https://twitter.com/joshbloch/status/583813919019573248
Przepraszam za odpowiedź, że nie jest tak pouczająca jak inne odpowiedzi, ale pomyślałem, że będzie to najbardziej interesujące i zrozumiałe.
źródło
ArrayList
jest losowo dostępny, a jednocześnieLinkedList
jest bardzo tani w rozszerzaniu i usuwaniu elementów. W większości przypadkówArrayList
jest w porządku.Jeśli nie utworzysz dużych list i nie zmierzysz wąskiego gardła, prawdopodobnie nigdy nie będziesz musiał się martwić różnicą.
źródło
TL; DR dzięki nowoczesnej architekturze komputerowej
ArrayList
będzie znacznie wydajniejszy w prawie każdym możliwym przypadku użycia - i dlategoLinkedList
należy go unikać, z wyjątkiem niektórych bardzo wyjątkowych i ekstremalnych przypadków.Teoretycznie LinkedList ma O (1) dla
add(E element)
Również dodanie elementu na środku listy powinno być bardzo wydajne.
Praktyka jest bardzo inna, ponieważ LinkedList jest strukturą wrogich danych w pamięci podręcznej . Z wydajności POV - w bardzo niewielu przypadkach wydajność
LinkedList
może być lepsza niż w przypadku pamięci podręcznejArrayList
.Oto wyniki testu porównawczego wstawiania elementów w losowych lokalizacjach. Jak widać - lista tablic jest znacznie bardziej wydajna, chociaż teoretycznie każda wstawka na środku listy będzie wymagała „przesunięcia” n późniejszych elementów tablicy (im niższe wartości, tym lepiej):
Praca na sprzęcie nowej generacji (większe, wydajniejsze pamięci podręczne) - wyniki są jeszcze bardziej rozstrzygające:
LinkedList zajmuje znacznie więcej czasu, aby wykonać tę samą pracę. źródło kod źródłowy
Istnieją dwa główne powody:
Głównie - że węzły
LinkedList
są rozproszone losowo w pamięci. RAM („Random Access Memory”) nie jest tak naprawdę losowy i bloki pamięci muszą zostać pobrane do pamięci podręcznej. Ta operacja wymaga czasu, a gdy takie pobieranie często się zdarza - strony pamięci w pamięci podręcznej muszą być cały czas wymieniane -> Brak pamięci podręcznej -> Pamięć podręczna nie jest wydajna.ArrayList
elementy są przechowywane w ciągłej pamięci - właśnie do tego optymalizuje się nowoczesna architektura procesora.Wtórne
LinkedList
wymagane do zatrzymania wskaźników do tyłu / do przodu, co oznacza 3-krotne zużycie pamięci na zapamiętaną wartość w porównaniu doArrayList
.DynamicIntArray , btw, jest niestandardowym holdingiem implementującym ArrayList
Int
(typ pierwotny), a nie Obiekty - stąd wszystkie dane są naprawdę przechowywane w sąsiedztwie - a więc jeszcze bardziej wydajne.Kluczowym elementem, o którym należy pamiętać, jest to, że koszt pobrania bloku pamięci jest większy niż koszt dostępu do pojedynczej komórki pamięci. Dlatego czytnik 1 MB pamięci sekwencyjnej jest do 400 razy szybszy niż odczyt tej ilości danych z różnych bloków pamięci:
Źródło: Liczby opóźnień, które powinien znać każdy programista
Aby wyjaśnić sprawę jeszcze bardziej, sprawdź poziom dodawania elementów na początku listy. Jest to przypadek użycia, w którym teoretycznie
LinkedList
powinien naprawdę błyszczeć iArrayList
powinien dawać słabe lub nawet gorsze wyniki:Uwaga: jest to test porównawczy biblioteki C ++ Std lib, ale moje wcześniejsze doświadczenia wykazały, że wyniki C ++ i Java są bardzo podobne. Kod źródłowy
Kopiowanie sekwencyjną większość pamięci jest operacją optymalizowane przez współczesnych procesorów - zmiana teorii i faktycznie co znowu
ArrayList
/Vector
dużo bardziej wydajnyKredyty: Wszystkie opublikowane tutaj testy porównawcze są tworzone przez Kjell Hedström . Jeszcze więcej danych można znaleźć na jego blogu
źródło
Jeśli twój kod ma
add(0)
iremove(0)
, użyj aLinkedList
i jest ładniejszeaddFirst()
iremoveFirst()
metod. W przeciwnym razie użyjArrayList
.I oczywiście, guawa „s ImmutableList jest twoim najlepszym przyjacielem.
źródło
Wiem, że to stary post, ale szczerze mówiąc, nie mogę uwierzyć, że nikt nie wspomniał o tym, że to
LinkedList
implementujeDeque
. Wystarczy spojrzeć na metody wDeque
(iQueue
); jeśli chcesz sprawiedliwego porównania, spróbuj uruchomićLinkedList
przedArrayDeque
i zrobić funkcja-for-funkcji porównania.źródło
Oto zapis Big-O w obu
ArrayList
iLinkedList
, a takżeCopyOnWrite-ArrayList
:ArrayList
Połączona lista
CopyOnWrite-ArrayList
Na tej podstawie musisz zdecydować, co wybrać. :)
źródło
LinkedList.add()
, chociaż większość odpowiedzi tutaj tak mówi.Porównajmy LinkedList i ArrayList wrt poniżej parametrów:
1. Wdrożenie
2. Wydajność
get (int index) lub operacja wyszukiwania
Powodem, dla którego ArrayList jest szybszy niż LinkedList, jest to, że ArrayList używa systemu opartego na indeksie dla swoich elementów, ponieważ z drugiej strony wewnętrznie wykorzystuje strukturę danych tablicowych,
LinkedList nie zapewnia dostępu opartego na indeksie dla swoich elementów, ponieważ wykonuje iterację od początku lub końca (w zależności od tego, co jest bliżej), aby pobrać węzeł o określonym indeksie elementu.
operacja wstawiania () lub dodawania (obiektu)
operacja remove (int)
Operacja usuwania na LinkedList jest zasadniczo taka sama jak ArrayList, tj. O (n).
3. Iterator wsteczny
4. Początkowa pojemność
5. Narzut pamięci
Źródło
źródło
Zwykle używam jednego nad drugim w oparciu o złożoność czasową operacji, które wykonałbym na tej konkretnej liście.
źródło
Oprócz innych dobrych argumentów powyżej, powinieneś zauważyć interfejs
ArrayList
implementującyRandomAccess
, podczas gdyLinkedList
implementujeQueue
.W jakiś sposób rozwiązują one nieco inne problemy, z różną wydajnością i zachowaniem (zobacz ich listę metod).
źródło
To zależy od tego, jakie operacje będziesz wykonywać więcej na liście.
ArrayList
jest szybszy dostęp do wartości indeksowanej. Jest znacznie gorzej podczas wstawiania lub usuwania obiektów.Aby dowiedzieć się więcej, przeczytaj każdy artykuł, który mówi o różnicy między tablicami i połączonymi listami.
źródło
Lista tablic jest zasadniczo tablicą z metodami dodawania elementów itp. (Zamiast tego należy użyć listy ogólnej). Jest to zbiór elementów, do których można uzyskać dostęp za pośrednictwem indeksu (na przykład [0]). Oznacza przejście z jednego elementu do drugiego.
Połączona lista określa przejście od jednego elementu do następnego (Pozycja a -> pozycja b). Możesz uzyskać ten sam efekt z listą tablic, ale lista połączona absolutnie mówi, który element powinien następować po poprzedniej.
źródło
Zobacz samouczki Java - implementacje list .
źródło
Ważną cechą połączonej listy (której nie przeczytałem w innej odpowiedzi) jest połączenie dwóch list. W przypadku tablicy jest to O (n) (+ narzut niektórych ponownych alokacji), a połączona lista to tylko O (1) lub O (2) ;-)
Ważne : w przypadku Javy
LinkedList
nie jest to prawdą! Zobacz Czy istnieje szybka metoda konkat dla listy połączonej w Javie?źródło
next
z jednej listy pierwszego węzła na drugiej liście. Jedynym sposobem jest użycieaddAll()
sekwencyjnego dodawania elementów, chociaż jest to lepsze niż zapętlenie i wywołanieadd()
każdego elementu. Aby to zrobić szybko w O (1), potrzebujesz klasy kompozycyjnej (takiej jak org.apache.commons.collections.collection.CompositeCollection), ale wtedy działałoby to dla dowolnego rodzaju List / Collection.ArrayList i LinkedList mają swoje zalety i wady.
ArrayList używa ciągłego adresu pamięci w porównaniu do LinkedList, który używa wskaźników do następnego węzła. Kiedy więc chcesz wyszukać element w ArrayList, jest to szybsze niż wykonywanie iteracji za pomocą LinkedList.
Z drugiej strony, wstawianie i usuwanie z LinkedList jest znacznie łatwiejsze, ponieważ wystarczy zmienić wskaźniki, podczas gdy ArrayList implikuje użycie operacji shift do dowolnego wstawiania lub usuwania.
Jeśli masz częste operacje pobierania w swojej aplikacji, użyj ArrayList. Jeśli masz częste wstawianie i usuwanie, skorzystaj z LinkedList.
źródło
Przeczytałem odpowiedzi, ale jest jeden scenariusz, w którym zawsze używam LinkedList zamiast ArrayList, którym chcę się podzielić, aby usłyszeć opinie:
Za każdym razem, gdy miałem metodę, która zwraca listę danych uzyskanych z DB, zawsze używam LinkedList.
Moim uzasadnieniem było to, że ponieważ nie można dokładnie wiedzieć, ile wyników otrzymuję, nie zostanie zmarnowana pamięć (jak w ArrayList z różnicą między pojemnością a rzeczywistą liczbą elementów) i nie będzie marnowania czasu na próby zduplikuj pojemność.
Jeśli chodzi o ArrayList, zgadzam się, że przynajmniej powinieneś zawsze używać konstruktora z początkową pojemnością, aby zminimalizować powielanie tablic w jak największym stopniu.
źródło
ArrayList
aLinkedList
oba narzędzia,List interface
ich metody i wyniki są prawie identyczne. Jednak istnieje kilka różnic między nimi, które sprawiają, że jedna jest lepsza od drugiej w zależności od wymagań.ArrayList Vs LinkedList
1)
Search:
ArrayList
operacja wyszukiwania jest dość szybka w porównaniu doLinkedList
operacji wyszukiwania.get(int index)
inArrayList
daje wydajność,O(1)
podczas gdyLinkedList
wydajność jestO(n)
.Reason:
ArrayList
utrzymuje system oparty na indeksach dla swoich elementów, ponieważ niejawnie wykorzystuje strukturę danych tablicowych, co przyspiesza wyszukiwanie elementu na liście. Z drugiej stronyLinkedList
implementuje podwójnie połączoną listę, która wymaga przejścia przez wszystkie elementy w celu wyszukania elementu.2)
Deletion:
LinkedList
operacja usuwania dajeO(1)
wydajność, a wydajnośćArrayList
zmienną:O(n)
w najgorszym przypadku (podczas usuwania pierwszego elementu),O(1)
aw najlepszym przypadku (podczas usuwania ostatniego elementu).Powód: każdy element LinkedList utrzymuje dwa wskaźniki (adresy), które wskazują oba sąsiednie elementy na liście. Dlatego usunięcie wymaga jedynie zmiany położenia wskaźnika w dwóch sąsiednich węzłach (elementach) węzła, który ma zostać usunięty. Podczas gdy w ArrayList wszystkie elementy muszą zostać przesunięte, aby wypełnić przestrzeń utworzoną przez usunięty element.
3)
Inserts Performance:
LinkedList
metoda add dajeO(1)
wydajność, aw najgorszym przypadkuArrayList
dajeO(n)
. Powód jest taki sam, jak wyjaśniony dla usunięcia.4)
Memory Overhead:
ArrayList
utrzymuje indeksy i dane elementu,LinkedList
zachowując dane elementu i dwa wskaźniki dla sąsiednich węzłówIstnieje kilka podobieństw między tymi klasami, które są następujące:
iterator
ilistIterator
zwrócone przez te klasy sąfail-fast
(jeśli lista jest modyfikowana strukturalnie w dowolnym momencie po utworzeniu iteratora, w jakikolwiek sposób pozaiterator’s
własnymi metodami usuwania lub dodawania, iterator zrobithrow
toConcurrentModificationException
).Kiedy używać LinkedList, a kiedy ArrayList?
(O(1))
wLinkedList
porównaniu doArrayList(O(n))
.get method
Operacje wyszukiwania ( ) są szybkie w,Arraylist (O(1))
ale nie wLinkedList (O(n))
źródło
Operacja get (i) w ArrayList jest szybsza niż LinkedList, ponieważ:
ArrayList: Implementacja tablic o zmiennym rozmiarze dla interfejsu List
LinkedList: Podwójnie połączona implementacja list interfejsów List i Deque
Operacje, które indeksują do listy, będą przechodzić przez listę od początku lub na końcu, w zależności od tego, który jest bliżej określonego indeksu.
źródło
1) Podstawowa struktura danych
Pierwsza różnica między ArrayList i LinkedList polega na tym, że ArrayList jest wspierany przez Array, podczas gdy LinkedList jest wspierany przez LinkedList. Doprowadzi to do dalszych różnic w wydajności.
2) LinkedList implementuje Deque
Inną różnicą między ArrayList i LinkedList jest to, że oprócz interfejsu List, LinkedList implementuje również interfejs Deque, który zapewnia operacje „pierwsze przy pierwszym” dla funkcji add () i poll () oraz kilku innych funkcji Deque. 3) Dodawanie elementów w ArrayList Dodawanie elementu w ArrayList jest operacją O (1), jeśli nie powoduje zmiany rozmiaru tablicy, w którym to przypadku staje się O (log (n)). Z drugiej strony, dodanie elementu w LinkedList jest operacją O (1), ponieważ nie wymaga nawigacji.
4) Usuwanie elementu z pozycji
Aby usunąć element z określonego indeksu, np. Wywołując remove (index), ArrayList wykonuje operację kopiowania, która zbliża go do O (n), podczas gdy LinkedList musi przejść do tego punktu, co również powoduje, że O (n / 2) , ponieważ może przechodzić z obu kierunków w oparciu o bliskość.
5) Iterowanie po ArrayList lub LinkedList
Iteracja jest operacją O (n) zarówno dla LinkedList, jak i ArrayList, gdzie n jest liczbą elementów.
6) Pobieranie elementu z pozycji
Operacja get (indeks) to O (1) w ArrayList, a O (n / 2) w LinkedList, ponieważ musi przechodzić do tego wpisu. Jednak w zapisie Big O O (n / 2) jest po prostu O (n), ponieważ tam ignorujemy stałe.
7) Pamięć
LinkedList używa obiektu opakowującego, Entry, który jest statycznie zagnieżdżoną klasą do przechowywania danych i dwóch węzłów obok siebie i wcześniej, podczas gdy ArrayList po prostu przechowuje dane w Array.
Zatem zapotrzebowanie na pamięć wydaje się mniejsze w przypadku ArrayList niż LinkedList, z wyjątkiem przypadku, gdy Array wykonuje operację zmiany rozmiaru, gdy kopiuje zawartość z jednej tablicy do drugiej.
Jeśli tablica jest wystarczająco duża, może w tym momencie zająć dużo pamięci i uruchomić zbieranie śmieci, co może spowolnić czas odpowiedzi.
Biorąc pod uwagę wszystkie powyższe różnice między ArrayList a LinkedList, wygląda na to, że ArrayList jest lepszym wyborem niż LinkedList w prawie wszystkich przypadkach, z wyjątkiem przypadków wykonywania częstej operacji add () niż remove () lub get ().
Łatwiej jest zmodyfikować listę połączoną niż ArrayList, szczególnie jeśli dodajesz lub usuwasz elementy od początku lub końca, ponieważ lista połączona wewnętrznie przechowuje odniesienia do tych pozycji i są one dostępne w czasie O (1).
Innymi słowy, nie musisz przechodzić przez połączoną listę, aby osiągnąć pozycję, w której chcesz dodać elementy, w takim przypadku dodawanie staje się operacją O (n). Na przykład wstawianie lub usuwanie elementu na środku połączonej listy.
Moim zdaniem używaj ArrayList zamiast LinkedList do większości praktycznych celów w Javie.
źródło
Jeden z testów, które tu widziałem, przeprowadza test tylko raz. Zauważyłem jednak, że musisz uruchamiać te testy wiele razy, a ich czasy w końcu się zbiegną. Zasadniczo JVM musi się rozgrzać. W moim szczególnym przypadku użycia musiałem dodać / usunąć elementy z listy, która rośnie do około 500 elementów. W moich testach
LinkedList
wyszło szybciej, przyLinkedList
około 50 000 NS iArrayList
około 90 000 NS ... dawaj lub bierz. Zobacz poniższy kod.źródło
Zarówno remove (), jak i insert () mają wydajność środowiska wykonawczego O (n) zarówno dla ArrayLists, jak i LinkedLists. Przyczyna liniowego czasu przetwarzania wynika jednak z dwóch bardzo różnych przyczyn:
W ArrayList dostajesz się do elementu w O (1), ale w rzeczywistości usunięcie lub wstawienie czegoś powoduje, że O (n), ponieważ wszystkie poniższe elementy muszą zostać zmienione.
Na LinkedList potrzeba O (n), aby dostać się do pożądanego elementu, ponieważ musimy zacząć od samego początku, aż osiągniemy pożądany indeks. W rzeczywistości usuwanie lub wstawianie jest stałe, ponieważ musimy zmienić tylko 1 referencję dla remove () i 2 referencje dla insert ().
To, który z nich jest szybszy do wstawiania i usuwania, zależy od tego, gdzie to się dzieje. Jeśli jesteśmy bliżej początku, LinkedList będzie szybszy, ponieważ musimy przejść przez stosunkowo niewiele elementów. Jeśli jesteśmy bliżej końca, ArrayList będzie szybszy, ponieważ docieramy tam w stałym czasie i musimy tylko zmienić kilka pozostałych elementów, które za nim podążają. Po wykonaniu dokładnie pośrodku LinkedList będzie szybszy, ponieważ przejście przez n elementów jest szybsze niż przeniesienie n wartości.
Bonus: Chociaż nie ma możliwości zrobienia tych dwóch metod O (1) dla ArrayList, w rzeczywistości istnieje sposób, aby to zrobić w LinkedLists. Powiedzmy, że chcemy przejść przez całą Listę, usuwając i wstawiając elementy po drodze. Zwykle zaczynasz od samego początku dla każdego elementu za pomocą LinkedList, możemy również „zapisać” bieżący element, nad którym pracujemy z Iteratorem. Za pomocą Iteratora uzyskujemy wydajność O (1) dla remove () i insert () podczas pracy na LinkedList. Czyniąc to jedyną korzyścią dla wydajności, o której wiem, gdzie LinkedList jest zawsze lepsza niż ArrayList.
źródło
ArrayList rozszerza AbstractList i implementuje interfejs listy. ArrayList to tablica dynamiczna.
Można powiedzieć, że został stworzony w celu przezwyciężenia wad tablic
. Klasa LinkedList rozszerza AbstractSequentialList i implementuje interfejs List, Deque i Queue.
Wydajność
arraylist.get()
wynosi O (1), natomiastlinkedlist.get()
O (n)arraylist.add()
oznacza O (1), alinkedlist.add()
0 (1)arraylist.contains()
oznacza O (n) ilinkedlist.contains()
oznacza O (n)arraylist.next()
oznacza O (1), alinkedlist.next()
oznacza O (1)arraylist.remove()
oznacza O (n) podczas gdylinkedlist.remove()
jest O (1)W arraylist
iterator.remove()
jest O (n),podczas gdy w Linkedlist
iterator.remove()
jest O (1)źródło