Java'da ArrayList üzerinden LinkedList ne zaman kullanılır?


3122

Her zaman sadece kullanmak için bir oldum:

List<String> names = new ArrayList<>();

Arayüzü taşınabilirlik için tür adı olarak kullanıyorum , böylece bunlar gibi sorular sorduğumda kodumu yeniden çalışabilirim.

Tüm gereken LinkedListüzerinde kullanılabilir ArrayListve bunun tersi de?



1
LinkedList stackoverflow.com/a/42529652/2032701'in yazarından gelen alıntıya bakın ve bu konuda pratik bir fikir edineceksiniz .
Ruslan

Farkları açıklayan ve bazı performans testlerini içeren Java LinkedList ve ArrayList gönderisine bakın .
alonana

Yanıtlar:


3371

Özet ArrayList ile ArrayDequede tercih edilir birçok daha kullanım senaryoları LinkedList. Emin değilseniz - sadece başlayın ArrayList.


LinkedListve ArrayListListe arayüzünün iki farklı uygulamasıdır. LinkedListbunu iki bağlantılı bir liste ile uygular.ArrayListdinamik olarak yeniden boyutlandırma dizisiyle uygular.

Standart bağlantılı liste ve dizi işlemlerinde olduğu gibi, çeşitli yöntemler farklı algoritmik çalışma sürelerine sahip olacaktır.

İçin LinkedList<E>

  • get(int index)bir O (n) ile ( n / 4 ortalama adımları), ancak O (1) zaman index = 0ya da index = list.size() - 1(bu durumda, aynı zamanda kullanabilir getFirst()ve getLast()). Başlıca faydalarından biri LinkedList<E>
  • add(int index, E element)bir O (n) ile ( n / 4 ortalama adımları), ancak O (1) zaman index = 0ya da index = list.size() - 1(bu durumda, aynı zamanda kullanabilir addFirst()ve addLast()/ add()). Başlıca faydalarından biri LinkedList<E>
  • remove(int index)bir O (n) ile ( n / 4 ortalama adımları), ancak O (1) zaman index = 0ya da index = list.size() - 1(bu durumda, aynı zamanda kullanabilir removeFirst()ve removeLast()). Başlıca faydalarından biri LinkedList<E>
  • Iterator.remove()R , O (1) 'dir . Başlıca faydalarından biri LinkedList<E>
  • ListIterator.add(E element)R , O (1) 'dir . Başlıca faydalarından biri LinkedList<E>

Not: İşlemlerin çoğunda ortalama olarak n / 4 adım, en iyi durumda sabit adım sayısı (örn. İndex = 0) ve en kötü durumda n / 2 adım (listenin ortasında) gerekir

İçin ArrayList<E>

  • get(int index)R , O (1) 'dir . Ana faydası ArrayList<E>
  • add(E element)bir O (1) itfa, ancak O (n) en kötü durum dizi boyutlandırılır ve kopyalanmalıdır yana
  • add(int index, E element)bir O (n) ile ( n / 2 ortalama) adımları
  • remove(int index)bir O (n) ile ( n / 2 ortalama) adımları
  • Iterator.remove()bir O (n) ile ( n / 2 ortalama) adımları
  • ListIterator.add(E element)bir O (n) ile ( n / 2 ortalama) adımları

Not: İşlemlerin çoğunda ortalama olarak n / 2 adım, en iyi durumda (liste sonu) sabit adım sayısı, en kötü durumda n adım (listenin başlangıcı) gerekir

LinkedList<E>yineleyiciler kullanılarak sabit zamanlı ekleme veya çıkarma işlemlerine izin verir , ancak öğelere yalnızca sıralı erişim sağlar. Başka bir deyişle, listeyi ileri veya geri yürütebilirsiniz, ancak listede bir konum bulmak listenin boyutuyla orantılı zaman alır. Javadoc, "listeye indeksleyen işlemler listeyi baştan veya sondan, hangisi daha yakınsa seçecektir" diyor , bu nedenle bu yöntemler O (1) için ortalama olarak O (n) ( n / 4 adım) .index = 0

ArrayList<E>Öte yandan, hızlı rastgele okuma erişimine izin verin, böylece herhangi bir öğeyi sabit zamanda yakalayabilirsiniz. Ancak, uçtan başka bir yere ekleme veya çıkarma işlemi, bir açılış yapmak veya boşluğu doldurmak için tüm son elemanların kaydırılmasını gerektirir. Ayrıca altta yatan dizinin kapasitesinden fazla unsurları eklerseniz, yeni bir dizi (1.5 katı büyüklüğünde) tahsis edilir ve eski dizi böylece ekleyerek, yenisine kopyalanır ArrayListolan O (n) en kötü ancak ortalama sabit.

Bu yüzden yapmak istediğiniz işlemlere bağlı olarak, uygulamaları buna göre seçmelisiniz. Her iki Liste üzerinde de yineleme yapmak aynı derecede ucuzdur. (A üzerinde yineleme ArrayListyapmak teknik olarak daha hızlıdır, ancak gerçekten performansa duyarlı bir şey yapmazsanız, bunun için endişelenmemelisiniz - her ikisi de sabittir.)

LinkedListÖğeleri eklemek ve kaldırmak için mevcut yineleyicileri yeniden kullandığınızda ortaya çıkmanın temel faydaları . Bu işlemler daha sonra listeyi yalnızca yerel olarak değiştirerek O (1) ' de yapılabilir . Dizi listesinde, dizinin geri kalanının taşınması (yani kopyalanması) gerekir. Öte yandan, en kötü durum için O (n) ( n / 2 adım) ' LinkedListdaki bağlantıları takip eden bir yöntem arayışındayken , istenen bir pozisyonda matematiksel olarak hesaplanabilir ve O (1)' de erişilebilir .ArrayList

LinkedListListenin başına eklediğinizde veya listenin başından kaldırdığınızda ortaya çıkan bir başka avantaj da, bu işlemler O (1) , O (n) olduğu için ortaya çıkar ArrayList. Baş eklemek ve çıkarmak için ArrayDequeiyi bir alternatif olabileceğini unutmayın LinkedList, ancak a değildir List.

Ayrıca, büyük listeleriniz varsa, bellek kullanımının da farklı olduğunu unutmayın. A öğesinin her öğesi, LinkedListönceki ve sonraki öğelere işaretçiler de depolandığından daha fazla yüke sahiptir. ArrayListsBu yükü yok. Bununla birlikte, ArrayListsöğelerin gerçekten eklenip eklenmediğine bakılmaksızın, kapasite için ayrılan belleği doldurun.

A'nın varsayılan başlangıç ​​kapasitesi ArrayListoldukça küçüktür (Java 1.4 - 1.8'den 10). Ancak temel uygulama bir dizi olduğundan, çok sayıda öğe eklerseniz dizi yeniden boyutlandırılmalıdır. Çok fazla öğe ekleyeceğinizi bildiğinizde yeniden boyutlandırmanın yüksek maliyetinden kaçınmak için, ArrayListdaha yüksek başlangıç ​​kapasitesine sahip bir yapı oluşturun .


182
Kampanya maliyetlerinden bahsetmeyi unuttum. LinkedList'te, doğru konuma sahip olduğunuzda ekleme işlemi O (1) olurken, bir ArrayList'te O (n) değerine kadar yükselir - ekleme noktasını geçen tüm öğeler taşınmalıdır.
David Rodríguez - dribeas

26
Vector kullanımı ile ilgili olarak: Vector'e geri dönmeye gerek yoktur. Bunu yapmanın yolu, tercih edilen Liste uygulamanızla ve senkronize bir sarıcı vermek için, senkronize edilmiş bir çağrıdır. Bkz. Java.sun.com/docs/books/tutorial/collections/implementations/…
Ryan Cox

69
Hayır, LinkedList için pozisyonu bilseniz bile get hala O (n) olur, çünkü o pozisyona ulaşmak için temel uygulama, o pozisyonun değerine ulaşmak için bağlantılı listenin "sonraki" işaretleyicilerini yürümek zorundadır. Rasgele erişim diye bir şey yoktur. Konum 2 için, işaretçiler yürümek ucuz olabilir, ancak konum 1 milyon için çok ucuz değil. Mesele şu ki, konumla orantılıdır, yani O (n) anlamına gelir.
Jonathan Tran

53
@Kevin Anının "birbirine yakın" olması önemli olabilir. Donanım, bitişik bellek bloklarını (dinamik RAM) L1 veya L2 önbelleğinde daha hızlı statik RAM'e önbelleğe alır. Teoride ve çoğu zaman pratik olarak, bellek rastgele erişim olarak ele alınabilir. Ancak gerçekte, sırayla bellekte okumak rastgele sıraya göre biraz daha hızlı olacaktır. Performans açısından kritik bir döngü için bu önemli olabilir. Buna "mekansal yerellik" ya da referans yerellik derler .
Jonathan Tran

92
O(n/2)Veya diye bir şey yoktur O(n/4). Büyük O gösterimi, bir işlemin daha büyük n ile nasıl ölçeklendiğini gösterir . ve adım gerektiren bir işlem tam olarak adım gerektiren bir işlem gibi ölçeklenir , bu nedenle sabit toplamlar veya faktörler kaldırılır. ve ikisi de sadece . ve yine de farklı sabit faktörlere sahip olduklarından, bir tanesini diğerinden biriyle karşılaştırmak mümkün olmaz , her ikisi de sadece doğrusal ölçeklendirme işlemlerini gösterir. n/2nO(n/2)O(n/4)O(n)LinkedListArrayListO(n/2)O(n/4)
Holger

630

Şimdiye kadar, hiç kimse bu listelerin her birinin bellek ayak izini ele alamamış gibi genel fikir birliğinin yanı sıra a'dan LinkedListçok "çok daha fazla" olduğu ArrayListiçin, her iki listenin de N null referanslar için tam olarak ne kadar aldığını göstermek için bir miktar çatırdama yaptım.

Referansları göreli sistemlerinde 32 veya 64 bit (boş olsa bile) olduğundan, 32 ve 64 bit için 4 veri kümesi ekledim LinkedListsve ArrayLists.

Not:ArrayList Çizgiler için gösterilen boyutlar kesilmiş listeler içindir - Uygulamada, bir arka plandaki yedekleme dizisinin kapasitesi ArrayListgenellikle geçerli öğe sayısından daha büyüktür.

Not 2: (teşekkürler BeeOnRope) SıkıştırılmışOp'lar artık JDK6'nın ortasından itibaren ve varsayılan olarak, 64-bit makineler için aşağıdaki değerler, elbette özellikle kapatmazsanız, temel olarak 32-bit eşdeğerleriyle eşleşecektir.


LinkedList ve ArrayList Eleman Sayısı Grafiği x Bayt


Sonuç , özellikle çok yüksek bir eleman sayımı ile, bunun çok LinkedListdaha fazla olduğunu açıkça göstermektedir ArrayList. Bellek bir faktörse, yönlendirmekten kaçının LinkedLists.

Kullandığım formüller takip ediyor, yanlış bir şey yaptıysam ve düzelteceğimde bana bildirin. 'b' 32 veya 64 bit sistemler için 4 veya 8'dir ve 'n' eleman sayısıdır. Modların nedeninin, java'daki tüm nesnelerin, kullanılmasına bakılmaksızın 8 baytlık katları kaplayacağı için dikkat edin.

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)

Bağlantılı liste:

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)


2
LinkedList'in tek bir öğeyi saklamak için ArrayList kadar bellek gerektirdiğini görmek oldukça ilginç. Ne kadar sezgisel değil! Örneğinizi -XX: + UseCompressedOops ile çalıştırırsanız ne olur?
jontejj

215
Matematiğinizdeki sorun, grafiğinizin etkisini büyük ölçüde abartmasıdır. Her biri yalnızca int4 veya 8 bayt veri içeren nesneleri modelliyorsunuz. Bağlantılı listede, temelde 4 adet "ek yük" vardır. Bu nedenle grafiğiniz, bağlantılı listelerin dizi listelerinin depolanmasını "beş kat" kullandığı izlenimini verir. Bu yanlış. Tepegöz, ölçekleme faktörü değil, ek bir ayarlama olarak nesne başına 16 veya 32 bayttır.
Heath Hunnicutt

6
ArrayList / LinkedList / Node nesnelerinin hiçbiri yalnızca int içermez, bu yüzden orada söylediklerinizi alamıyorum. Açıklığa kavuşturmak için 'nesne başlığını' yeniden 'nesne başlığına' yeniden yazdım - sistemden bağımsız olarak her nesne için 8 baytlık bir başlık var ve evet, bağlantılı olabildiğince doğru sayılan tüm Düğüm nesnelerini içeriyor. söylemek. Bu arada, tekrar baktığımda, LinkedL'deki matemimle aslında bölmeyi ve ArrayList'i daha da kötüleştiren birkaç başka sorun buldum . Güncellemeye devam etmekten mutluluk duyuyorum, bu yüzden lütfen daha fazla netleştirmek ve ayrıntılandırmak için tereddüt etmeyin.
Numeron

6
Unutulmamalıdır CompressedOopsvarsayılan tüm son JDKs (7, 8 ve birkaç yıl 6 güncellemelerini), bu yüzden 64 bit bir fark yapmaz artık ArrayListya LinkedListaçıkça sıkıştırılmış oops kapatmadıysanız, boyutları bir sebep.
BeeOnRope

1
@jontejj varsayılan kapasite artışı% 50'dir, bu nedenle ArrayListbir başlangıç ​​kapasitesi belirtmeden doldurduğunuzda, a'dan önemli ölçüde daha az bellek kullanır LinkedList.
Holger

243

ArrayLististediğin şey bu. LinkedListneredeyse her zaman bir (performans) hatasıdır.

Neden LinkedListberbat:

  • Çok sayıda küçük bellek nesnesi kullanır ve bu nedenle işlem boyunca performansı etkiler.
  • Çok sayıda küçük nesne önbellek konumu açısından kötüdür.
  • İndekslenmiş herhangi bir işlem bir çapraz geçiş gerektirir, yani O (n) performansına sahiptir. Bu, kaynak kodda belirgin değildir ve O (n) algoritmalarının ArrayListkullanıldığından daha yavaş olmasına neden olur .
  • İyi performans elde etmek zordur.
  • Big-O performansı ile aynı olsa bile ArrayList, muhtemelen yine de önemli ölçüde daha yavaş olacaktır.
  • LinkedListKaynakta görmek zordur çünkü muhtemelen yanlış seçimdir.

236
Afedersiniz. sizi işaretledi. LinkedList emmez. LinkedList'in kullanılacak doğru sınıf olduğu durumlar vardır. Bir arraylistten daha iyi olduğu birçok durum olmadığına katılıyorum, ancak varlar. Aptalca şeyler yapan insanları eğit!
David Turner

40
Bunun için çok fazla oy aldığınız için üzgünüm. Gerçekten de Java'nın LinkedList'ini kullanmak için çok az neden var. Kötü performansa ek olarak, diğer somut List sınıflarından çok daha fazla bellek kullanır (her düğümün iki ek işaretçisi vardır ve her düğüm, onlarla birlikte gelen ek havai baytlara sahip ayrı bir sarma nesnesidir).
Kevin Brock

42
Bu, burada en yararlı cevaplardan biridir. Pek çok programcının (a) soyut veri türleri ve somut uygulamalar arasındaki farkı ve (b) performans belirlemede sabit faktörlerin ve bellek yükünün gerçek dünyadaki önemini anlayamaması çok utanç verici.
Porculus

50
-1: Bu oldukça göz kırpan bir görüş. Evet, ArrayList'in çok yönlü bir araç olduğu doğrudur. Ancak, sınırlamaları vardır. Bunun size sorun yaratacağı durumlar vardır ve LinkedList'i kullanmanız gerekecektir. Tabii ki, çok özel bir çözümdür ve herhangi bir özel araç olarak, çoğu durumda çok yönlü bir çözümden daha iyi performans gösterir. Ama bu "berbat" ya da bunun gibi bir şey olduğu anlamına gelmez, sadece ne zaman kullanacağınızı bilmeniz gerekir.
Malcolm

27
@DavidTurner: Varlar ama sanırım Tom'un amacı, sormak zorunda kalırsanız muhtemelen ArrayList'i istemenizdi.
user541686

139

Yaklaşık on yıldır çok büyük ölçekli SOA web hizmetlerinde operasyonel performans mühendisliği yapan biri olarak, LinkedList'in ArrayList yerine davranışını tercih ederim. LinkedList'in kararlı durum çıktısı daha kötüdür ve bu nedenle daha fazla donanım satın almanıza neden olabilirken, ArrayList'in baskı altındaki davranışı, bir kümedeki uygulamaların dizilerini neredeyse eşzamanlılıkta ve büyük dizi boyutları için genişletmesine neden olabilir. uygulamada ve bir kesintide, baskı altındayken, bu felaket davranışıdır.

Benzer şekilde, bir uygulamada varsayılan işlem süresi garantili çöp toplayıcıdan daha iyi verim alabilirsiniz, ancak 10 GB yığınları olan java uygulamaları aldığınızda, Tam GC'ler sırasında uygulamayı SOA uygulamalarında zaman aşımlarına ve arızalara neden olan 25 saniye boyunca kilitleyebilirsiniz. ve çok sık meydana gelirse SLA'larınızı üfler. CMS toplayıcı daha fazla kaynak alsa ve aynı ham verimi elde etmese de, daha öngörülebilir ve daha az gecikmeye sahip olduğu için çok daha iyi bir seçimdir.

ArrayList, performans için kast ettiğiniz tek şey iş hacmiyse ve gecikmeyi göz ardı edebiliyorsanız performans için daha iyi bir seçimdir. İşimdeki tecrübelerime göre en kötü durum gecikmesini görmezden gelemem.


8
ArrayList'in sureCapacity () yöntemini kullanarak başka bir çözüm listenin boyutunu programlı olarak yönetemez mi? Benim sorum neden bu kadar çok şey önbellek veya db mekanizmasında daha iyi saklanabilir zaman bir grup kırılgan veri yapılarında saklanıyor? Geçen gün ArrayList'in kötülükleri hakkında küfür ettikleri bir röportaj yaptım, ama buraya geliyorum ve karmaşıklık analizinin çok daha iyi olduğunu gördüm! TARTIŞMA İÇİN BÜYÜK NOKTA, DÜŞÜNCE. TEŞEKKÜRLER!
Ekim'de

22
10GB yığınları olan java uygulamalarını aldıktan sonra, zaman aşımlarına neden olan bir Tam GC sırasında uygulamayı 25 saniye boyunca kilitleyebilirsiniz . her düğüm.
bestsss

5
Bu ... korkunç bir çözüm. sen ... sadece bunun yerine bir arraylist üzerinde ensureCapacity () çağırabilir zaman, inanılmaz pahalı sizin için temizlik GC, üzerinde temelde güvenen konum
Philip Devine

5
@Andreas: A, LinkedList her zaman düz bir referans dizisinden beş kat daha fazla bellek ayırır, bu nedenle ArrayListgeçici olarak 2,5 kez gerektiren bir bellek, geri kazanılmamış olsa bile çok daha az bellek tüketir. Büyük dizi ayırma Eden alanını atladığından, gerçekten yeterli bellek yoksa, GC davranışı üzerinde herhangi bir etkisi yoktur, bu durumda, LinkedListçok daha erken patladı ...
Holger

5
@Andreas Diğer sorun, belleğin nasıl tahsis edildiği. LinkedListsonraki öğeye ayırmak için yalnızca küçük bir boş hafıza parçasına ihtiyaç duyar. yeniden boyutlandırılan diziyi ayırmak için ArrayListgeniş ve sürekli bir boş alan bloğu gerekir . Yığın parçalanırsa GC, uygun bir tek bellek bloğunu boşaltmak için tüm yığını yeniden sıralayabilir.
Piotr Kusmierczyk

128
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

Algoritmalar: Büyük Oh Gösterimi

ArrayLists, bir kez okunan çok sayıda veya eklenti için iyidir, ancak önden veya ortadan ekleme / çıkarma konusunda kötüdür.


42
Sabit faktörleri düşünmeden big-O değerlerini doğrudan karşılaştıramazsınız. Küçük listeler için (ve çoğu liste küçüktür), ArrayList'in O (N), LinkedList'in O (1) 'den daha hızlıdır.
Porculus

4
Ben küçük listeleri performansı önemsemez, ve ne bilgisayarımı yapar yok sürece bu şekilde bir döngüde kullanılır.
Maarten Bodewes

45
LinkedList tam ortada ekleyemez O(1). Ekleme noktasını bulmak için listenin yarısından geçmelidir.
Thomas Ahle

8
LinkedList: orta O (1) içine yerleştirin - YANLIŞ! LinkedList boyutunun 1 / 10'luk konumuna bile eklemenin, bir ArrayList'in 1 / 10'uncu konumuna bir öğe eklemekten daha yavaş olduğunu öğrendim. Ve daha da kötüsü: koleksiyonun sonu. ArrayList'in son konumlarına (en son değil)
eklemek

14
Bir de @kachanov ekleme LinkedList olduğunu O(1) sen ekleme pozisyonuna bir yineleyici varsa , yani ListIterator.addsözde olduğu O(1)bir için LinkedList.
ÇIKIŞ - Anony-Mousse

107

Evet, biliyorum, bu eski bir soru, ama iki sentimi atacağım:

LinkedList neredeyse her zaman yanlış seçimdir, performans açısından. LinkedList'in çağrıldığı bazı çok spesifik algoritmalar vardır, ancak bunlar çok, çok nadirdir ve algoritma genellikle LinkedList'in oraya gittiğinizde listenin ortasındaki öğeleri nispeten hızlı bir şekilde ekleme ve silme yeteneğine bağlı olacaktır. bir ListIterator ile.

LinkedList'in ArrayList'ten daha iyi performans gösterdiği yaygın bir kullanım durumu vardır: bir kuyruğunkinden. Ancak, hedefiniz performanssa, LinkedList yerine ArrayBlockingQueue (sıra boyutunuzda önceden bir üst sınır belirleyebiliyorsanız ve tüm belleği öne atayabilirseniz) veya bu CircularArrayList uygulamasını kullanmayı düşünmelisiniz. . (Evet, 2001'den beri, bunu doğrulamanız gerekecek, ancak kısa bir süre önce yeni bir JVM'de makalede alıntılananlarla karşılaştırılabilir performans oranları aldım)


39

1
ArrayDequeLinkedListtüm işlemler aynı sonda olmadıkça daha yavaştır . Yığın olarak kullanıldığında sorun yoktur, ancak iyi bir kuyruk oluşturmaz.
Jeremy List

2
Gerçek - en azından Oracle'ın jdk1.7.0_60 ve sonraki testteki uygulamaları için. 10 milyon kez döngü yaptığım bir test oluşturdum ve 10 milyon rastgele Tamsayı Deque'm var. Döngünün içinde bir elementi yoklar ve sabit bir element sunarım. Bilgisayarımda LinkedList, ArrayDeque'den 10 kat daha yavaş ve daha az bellek kullanıyor). Bunun nedeni, ArrayList'ten farklı olarak, ArrayDeque dizinin başına bir işaretçi tutar, böylece kafa kaldırıldığında tüm öğeleri taşımak zorunda kalmaz.
Henno Vermeulen

6
ArrayDequedaha hızlı olması muhtemeldir Stackbir yığın olarak kullanılabilir ve daha hızlı olduğunda LinkedListbir sıra olarak kullanıldığında.
akhil_mittal

3
Akhil_mittal'in yorumunun ArrayDequebelgelerden bir alıntı olduğunu unutmayın .
Stuart Marks


54

Doğru veya Yanlış: Lütfen testi yerel olarak yürütün ve kendiniz karar verin!

Düzenleme / Kaldır hızlı olduğundan LinkedListdaha ArrayList.

ArrayList, Arraybüyüklüğünün iki katı olması gereken, büyük hacimli uygulamalarda daha kötüdür.

Aşağıda her bir işlem için birim test sonucu verilmiştir. Zamanlama, Nanosaniye olarak verilmiştir.


Operation                       ArrayList                      LinkedList  

AddAll   (Insert)               101,16719                      2623,29291 

Add      (Insert-Sequentially)  152,46840                      966,62216

Add      (insert-randomly)      36527                          29193

remove   (Delete)               20,56,9095                     20,45,4904

contains (Search)               186,15,704                     189,64,981

İşte kod:

import org.junit.Assert;
import org.junit.Test;

import java.util.*;

public class ArrayListVsLinkedList {
    private static final int MAX = 500000;
    String[] strings = maxArray();

    ////////////// ADD ALL ////////////////////////////////////////
    @Test
    public void arrayListAddAll() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        arrayList.addAll(stringList);
        watch.totalTime("Array List addAll() = ");//101,16719 Nanoseconds
    }

    @Test
    public void linkedListAddAll() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);

        watch.start();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);
        watch.totalTime("Linked List addAll() = ");  //2623,29291 Nanoseconds
    }

    //Note: ArrayList is 26 time faster here than LinkedList for addAll()

    ///////////////// INSERT /////////////////////////////////////////////
    @Test
    public void arrayListAdd() {
        Watch watch = new Watch();
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        for (String string : strings)
            arrayList.add(string);
        watch.totalTime("Array List add() = ");//152,46840 Nanoseconds
    }

    @Test
    public void linkedListAdd() {
        Watch watch = new Watch();

        List<String> linkedList = new LinkedList<String>();
        watch.start();
        for (String string : strings)
            linkedList.add(string);
        watch.totalTime("Linked List add() = ");  //966,62216 Nanoseconds
    }

    //Note: ArrayList is 9 times faster than LinkedList for add sequentially

    /////////////////// INSERT IN BETWEEN ///////////////////////////////////////

    @Test
    public void arrayListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX + MAX / 10);
        arrayList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        arrayList.add(insertString0);
        arrayList.add(insertString1);
        arrayList.add(insertString2);
        arrayList.add(insertString3);

        watch.totalTime("Array List add() = ");//36527
    }

    @Test
    public void linkedListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        linkedList.add(insertString0);
        linkedList.add(insertString1);
        linkedList.add(insertString2);
        linkedList.add(insertString3);

        watch.totalTime("Linked List add = ");//29193
    }


    //Note: LinkedList is 3000 nanosecond faster than ArrayList for insert randomly.

    ////////////////// DELETE //////////////////////////////////////////////////////
    @Test
    public void arrayListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.remove(searchString0);
        arrayList.remove(searchString1);
        watch.totalTime("Array List remove() = ");//20,56,9095 Nanoseconds
    }

    @Test
    public void linkedListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.remove(searchString0);
        linkedList.remove(searchString1);
        watch.totalTime("Linked List remove = ");//20,45,4904 Nanoseconds
    }

    //Note: LinkedList is 10 millisecond faster than ArrayList while removing item.

    ///////////////////// SEARCH ///////////////////////////////////////////
    @Test
    public void arrayListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.contains(searchString0);
        arrayList.contains(searchString1);
        watch.totalTime("Array List addAll() time = ");//186,15,704
    }

    @Test
    public void linkedListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.contains(searchString0);
        linkedList.contains(searchString1);
        watch.totalTime("Linked List addAll() time = ");//189,64,981
    }

    //Note: Linked List is 500 Milliseconds faster than ArrayList

    class Watch {
        private long startTime;
        private long endTime;

        public void start() {
            startTime = System.nanoTime();
        }

        private void stop() {
            endTime = System.nanoTime();
        }

        public void totalTime(String s) {
            stop();
            System.out.println(s + (endTime - startTime));
        }
    }


    private String[] maxArray() {
        String[] strings = new String[MAX];
        Boolean result = Boolean.TRUE;
        for (int i = 0; i < MAX; i++) {
            strings[i] = getString(result, i);
            result = !result;
        }
        return strings;
    }

    private String getString(Boolean result, int i) {
        return String.valueOf(result) + i + String.valueOf(!result);
    }
}

1
Kesin olmak için ArrayList'in iki katına çıkarılmasına gerek yoktur. Lütfen önce kaynakları kontrol edin.
Danubian Sailor

Örneğinizin kusurlu olduğuna dikkat edilmelidir ... 18 + [2, 12] bayt ("true0false", "true500000false"), öğelerin boyutları olan ortalama 25 bayt arasında bir dizeden kaldırıyorsunuz ortada. Öğe bayt boyutu arttıkça bağlantılı listenin daha iyi performans gösterdiği, liste boyutu arttıkça bitişik bir dizinin (liste) daha iyi olacağı bilinmektedir. En önemlisi, dizelerde .equals () yapıyorsunuz - bu ucuz bir işlem değil. Bunun yerine tamsayılar kullandıysanız, bence bir fark olacaktır.
Centril

2
"... büyük hacimli uygulamalarda daha kötü ": Bu bir yanlış anlamadır. LinkedListher öğe için beş alana sahip bir düğüm nesnesi olduğundan daha fazla bellek yükü vardır. 20 bayt yükü yapan birçok sistemde. İçin öğe başına ortalama bellek yükü ArrayListbir buçuk kelimedir ve en kötü durumda 6 bayt ve 8 bayt yapar.
Lii

1
Burada kıyaslamanızın daha iyi bir versiyonunu yaptım , sonuçlarla - arraylist için uçtaki performans sizin için yapay olarak düşüktür, çünkü addAll, TAM başlangıç ​​boyutunda bir depolama dizisi veriyor, bu nedenle ilk ekleme her zaman bir arraycopy. Ayrıca, veri toplanmadan önce JIT derlemesine izin veren ısınma döngüleri de buna dahildir.
16:48, BobMcGee

4
@BillK Java 8'den beri, her bir öğe için geri kalanın tamamını kaydırmak gerekmediği için, yineleyici aracılığıyla döngü ve kaldırma ile karşılaştırıldığında, removeIf(element -> condition)uygun bir şekilde daha hızlı olabilen bir yerde kullanabilirsiniz ArrayList. Bunun teoride O (1) olduğu LinkedListgibi belirli senaryoya göre daha iyi veya kötü performans göstermesi LinkedList, ancak sadece tek bir düğümün ArrayListkaldırılması, önemli sayıda öğeyi kaldırırken ihtiyaç duyulan sayıyı kolayca aşabilecek birkaç bellek erişimi gerektirir . .
Holger

50

ArrayListaslında bir dizidir. LinkedListçift ​​bağlantılı bir liste olarak uygulanır.

getOldukça açıktır. O (1) için ArrayList, çünkü ArrayListindeks kullanarak rastgele erişime izin verin. O (n) için LinkedList, çünkü önce dizini bulması gerekir. Not: farklı sürümleri vardır addve remove.

LinkedListekleme ve kaldırmada daha hızlı, ancak almada daha yavaştır. Kısaca, aşağıdaki LinkedListdurumlarda tercih edilmelidir:

  1. çok sayıda rastgele eleman erişimi yok
  2. çok sayıda ekleme / çıkarma işlemi var

=== ArrayList ===

  • ekle (E e)
    • ArrayList'in sonuna ekle
    • bellek yeniden boyutlandırma maliyeti gerektirir.
    • O (n) en kötü, O (1) itfa edilmiş
  • ekle (int dizini, E öğesi)
    • belirli bir dizin konumuna ekle
    • kaydırma ve olası bellek yeniden boyutlandırma maliyeti gerektirir
    • O (n)
  • kaldır (int dizini)
    • belirtilen bir öğeyi kaldır
    • kaydırma ve olası bellek yeniden boyutlandırma maliyeti gerektirir
    • O (n)
  • kaldır (Object o)
    • belirtilen öğenin ilk oluşumunu bu listeden kaldır
    • önce öğeyi aramak ve daha sonra olası bellek yeniden boyutlandırma maliyetini değiştirmek gerekir
    • O (n)

=== LinkedList ===

  • ekle (E e)

    • listenin sonuna ekle
    • O (1)
  • ekle (int dizini, E öğesi)

    • belirtilen konumda takın
    • önce pozisyonu bulmalıyım
    • O (n)
  • Kaldırmak()
    • listenin ilk öğesini kaldır
    • O (1)
  • kaldır (int dizini)
    • belirtilen dizine sahip öğeyi kaldır
    • önce elemanı bulmalısın
    • O (n)
  • kaldır (Object o)
    • belirtilen öğenin ilk oluşumunu kaldır
    • önce elemanı bulmalısın
    • O (n)

Burada programcreek.com adresinden bir rakam bulunmaktadır ( addve removeilk türdür, yani listenin sonuna bir öğe ekleyin ve öğeyi listede belirtilen konumda kaldırın.):

resim açıklamasını buraya girin


3
Msgstr "LinkedList ekleme / kaldırma işleminden daha hızlı". Yanlış, yukarıdaki cevabı kontrol edin stackoverflow.com/a/7507740/638670
Nerrve

49

LinkedList'in yazarı Joshua Bloch:

LinkedList'i gerçekten kullanan var mı? Ben yazdım ve hiç kullanmadım.

Bağlantı: https://twitter.com/joshbloch/status/583813919019573248

Diğer cevaplar kadar bilgilendirici olmadığı için cevap için üzgünüm, ama bunun en ilginç ve açıklayıcı olacağını düşündüm.


34

ArrayListrastgele erişilebilir, ancak LinkedListöğeleri genişletmek ve kaldırmak için gerçekten ucuz. Çoğu durumda, ArrayListiyidir.

Büyük listeler oluşturmadıysanız ve bir darboğaz ölçmediyseniz, muhtemelen fark hakkında endişelenmenize gerek kalmayacak.


15
LinkedList'e öğe eklemek ucuz değildir. Bir ArrayList'e milyon eleman eklemek, bunları LinkedList'e eklemek neredeyse her zaman daha hızlıdır. Ve gerçek dünya kodundaki listelerin çoğu bile bir milyon element uzunluğunda değildir.
Porculus

10
Herhangi bir noktada, LinkedList'inize bir öğe eklemenin maliyetini bilirsiniz. ArrayList (genel olarak). Bir milyon öğeleri içeren bir ArrayList için tek bir öğe ekleme olabilir bir O (n) operasyonu artı boşluk İlk ayırma sürece depolama katına - çok uzun zaman alabilir. LinkedList'e öğe eklemek O (1) 'dir. Son ifadem duruyor.
Dustin

4
Bir ArrayList'e tek bir öğe eklemek, 1 milyon veya 1 milyar olursa olsun O (1) 'dir. LinkedList'e bir öğe eklemek de O (1) 'dir. "Ekleme", SONA EKLEME anlamına gelir.
kachanov

Uygulamayı benden farklı okumalısınız. Deneyimlerime göre, 1 milyar öğe dizisini kopyalamak 1 milyon öğe dizisini kopyalamaktan daha uzun sürüyor.
Dustin

6
@ kachanov Dustin'i yanlış anlamalısın. 1 milyar öğeden oluşan bir dizi bildirmedikçe, sonunda dizinizi yeniden boyutlandırmanız gerekir, bu durumda tüm öğeleri yeni bir daha büyük diziye kopyalamanız gerekir, bu nedenle bazen O (N) elde edersiniz, ancak bağlantılı bir listeyle her zaman O (1)
Stan R.

29

TL; Modern bilgisayar mimarisi nedeniyle DR , ArrayListneredeyse tüm olası kullanım durumları için önemli ölçüde daha verimli olacaktır ve bu nedenle LinkedListbazı benzersiz ve aşırı durumlar dışında kaçınılmalıdır.


Teorik olarak, LinkedList için O (1) add(E element)

Ayrıca listenin ortasına bir öğe eklemek çok verimli olmalıdır.

LinkedList bir Cache Hostile Data yapısı olduğu için uygulama çok farklıdır . Performans POV'sinden - Önbellek dostuLinkedList olmaktan daha iyi performans gösterebilecek çok az durum vardır . ArrayList

Rastgele konumlardaki eleman ekleme testlerinin sonuçları aşağıda verilmiştir. Gördüğünüz gibi - çok daha verimli ise dizi listesi, ancak teoride listenin ortasındaki her ekleme dizinin n sonraki öğelerini "taşımak" gerektirir (daha düşük değerler daha iyidir):

resim açıklamasını buraya girin

Yeni nesil bir donanım üzerinde çalışmak (daha büyük, daha verimli önbellekler) - sonuçlar daha da kesin:

resim açıklamasını buraya girin

LinkedList aynı işi yapmak için çok daha fazla zaman alır. kaynak Kaynak Kodu

Bunun iki ana nedeni vardır:

  1. Temel olarak - düğümlerinin LinkedListbellek boyunca rastgele dağılmasıdır. RAM ("Rasgele Erişim Belleği") gerçekten rastgele değildir ve önbellek için bellek bloklarının getirilmesi gerekir. Bu işlem zaman alır ve bu tür getirmeler sık ​​sık gerçekleştiğinde - önbellekteki bellek sayfalarının her zaman değiştirilmesi gerekir -> Önbellek özlüyor -> Önbellek verimli değildir. ArrayListöğeler sürekli bellekte depolanır - bu modern CPU mimarisinin tam olarak ne için optimizasyon yaptığıdır.

  2. İkincil LinkedList işaretçileri geri / ileri tutmak için gereklidir, bu da depolanan değer başına 3 kat daha fazla bellek tüketimi anlamına gelir ArrayList.

DynamicIntArray , btw, IntNesneler değil , özel bir ArrayList uygulama tutucusudur (ilkel tip) - dolayısıyla tüm veriler gerçekten bitişik olarak saklanır - dolayısıyla daha da verimli olur.

Hatırlanması gereken önemli bir unsur, bellek bloğunu getirmenin maliyetinin, tek bir bellek hücresine erişim maliyetinden daha önemli olmasıdır. Bu yüzden okuyucu 1MB sıralı bellek, farklı bellek bloklarından bu miktarda veri okumaktan x400 kat daha hızlıdır:

Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

Kaynak: Her Programcının Bilmesi Gereken Gecikme Sayıları

Sadece noktayı daha da netleştirmek için lütfen listenin başına öğe ekleme ölçütünü kontrol edin. Bu, teorik olarak, LinkedListgerçekten parlaması ArrayListgereken ve kötü veya daha kötü durumda sonuçlar sunması gereken bir kullanım durumudur:

resim açıklamasını buraya girin

Not: Bu, C ++ Std lib'in bir ölçütüdür, ancak önceki deneyimim C ++ ve Java sonuçlarının çok benzer olduğunu gösterdi. Kaynak kodu

Sıralı bir bellek yığınını kopyalamak, modern CPU'lar tarafından optimize edilmiş bir işlemdir - teoriyi değiştirmek ve aslında tekrar ArrayList/ Vectorçok daha verimli hale getirmek


Kredi: Burada yayınlanan tüm kriterler Kjell Hedström tarafından oluşturulur . Daha da veri bulunabilir blogunda


Bir kuyruğa benzersiz ya da aşırı demezdim! Bir fifo kuyruğu, ArrayList yerine LinkedList üzerinde daha kolay uygulanır. Aslında, kendi başlangıcınızı izlemek, durdurmak ve kendi yeniden tahsisinizi yapmak zorunda olduğunuz için bir ArrayList'te bir kabus, bir dizi de kullanabilirsiniz, ancak Bağlantılı Liste bir fifo'dur. Java'nın uygulaması hakkında emin değilim, ancak LinkedList hem kuyruk hem de dequeue işlemleri için O (1) yapabilirsiniz (java sahip olduğunu varsayalım kaldırmak için kuyruk elemanına özel bir işaretçi gerektirir, ancak çift kontrol etmedim .)
Bill K

24

Kodunuzda add(0)ve varsa remove(0), bir LinkedListve daha güzel addFirst()ve removeFirst()yöntemler kullanın. Aksi takdirde kullanın ArrayList.

Ve tabii ki, Guava 'in ImmutableList en iyi arkadaşınızdır.


3
Küçük listeler için ArrayList.add (0) her zaman LinkedList.addFirst () 'den daha hızlı olacaktır.
Porculus

1
@Porculus ArrayList.add (0) küçük listeler için daha hızlı olacak bu argümanı sürekli olarak duyuyorum, bu küçük ne kadar küçük? 10 element, 10 milyon,?
garg10may

1
@ garg10may small 10'dan küçük
Jesse Wilson

@Porculus small, ArrayList öğesinin altında yatan dahili dizinin maksimum kapasitesinden daha az anlamına gelir.
Janac Meena

21

Bunun eski bir yazı olduğunu biliyorum, ama dürüstçe kimsenin LinkedListuyguladığından bahsetmediğine inanamıyorum Deque. Sadece Deque(ve Queue) ' deki yöntemlere bakın ; Adil bir karşılaştırma yapmak istiyorsanız, LinkedListkarşı çalışmayı deneyin ArrayDequeve özellik için bir karşılaştırma yapın.


18

İşte hem Big-O gösterimi olan ArrayListve LinkedListayrıca CopyOnWrite-ArrayList:

ArrayList

get                 O(1)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

Bağlantılı liste

get                 O(n)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(1)
iterator.remove     O(1)

CopyOnWrite-ArrayList

get                 O(1)
add                 O(n)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

Bunlara dayanarak ne seçeceğinize karar vermelisiniz. :)


9
>>>> ArrayList add -> O (1) <- tru değil. Bazı durumlarda ArrayList'in bir öğe daha eklemek için büyümesi gerekecek
kachanov

1
LinkedList kaldırması O (1) değil, kaldırılacak öğeyi araması gerekir, bu nedenle en kötü durum O (n) ve ortalama O (n / 2)
garg10may

LinkedList.add()Buradaki yanıtların çoğu bunu söylese de hiçbiri değildir .
user207421

18

LinkedList ve ArrayList wrt parametrelerini aşağıdaki ile karşılaştıralım:

1. Uygulama

ArrayList , liste arayüzünün yeniden boyutlandırılabilir dizi uygulamasıdır.

LinkedList , liste arayüzünün Çift Bağlantılı liste uygulamasıdır.


2. Performans

  • get (int dizini) veya arama işlemi

    ArrayList get (int dizini) işlemi sabit zamanda çalışır, yani O (1)

    Bağlantılı liste get (int dizini) işlemi çalışma süresi O (n) 'dir.

    ArrayList'in arkasındaki neden LinkedList'ten daha hızlı olmasının , ArrayList'in dahili olarak bir dizi veri yapısı kullandığı için elemanları için dizin tabanlı bir sistem kullanmasıdır.

    LinkedList , belirtilen öğe dizinindeki düğümü almak için başlangıçtan veya sondan (hangisi daha yakınsa) yinelediğinden, öğeleri için dizin tabanlı erişim sağlamaz.

  • insert () veya add (Object) işlemi

    LinkedList'deki eklemeler genellikle ArrayList ile karşılaştırıldığında hızlıdır. LinkedList'te ekleme veya ekleme O (1) işlemidir.

    ArrayList'te iken , dizi tam yani en kötü durumda ise, dizi yeniden boyutlandırma ve öğeleri yeni diziye kopyalamanın ek bir maliyeti vardır, bu da ArrayList O (n) 'de ekleme işleminin çalışma zamanını yapar, aksi takdirde O (1) .

  • kaldır (int) işlemi

    LinkedList'teki kaldırma işlemi genellikle ArrayList yani O (n) ile aynıdır.

    In LinkedList , iki Aşırı yüklü kaldır yöntemi vardır. birincisi, listenin başlığını kaldıran ve sabit zaman O (1) ile çalışan herhangi bir parametre olmadan remove () 'dir. LinkedList'teki diğer aşırı yüklenmiş kaldırma yöntemi, parametre olarak iletilen Object veya int'i kaldıran remove (int) veya remove (Object) yöntemidir. Bu yöntem, Object'i bulana ve orijinal listeden bağlantısını kaldırana kadar LinkedList'i dolaşır. Bu nedenle, bu yöntem çalışma zamanı O (n) 'dir.

    İken ArrayList'in remove (int) yöntemi, yeni güncellenmiş bir dizi eski diziden elemanları kopyalama içeren bu nedenle çalışma zamanı O (n) 'dir.


3. Ters Yineleyici

LinkedList , descendingIterator () kullanılarak ters yönde yinelenebilir

ArrayList'te descendingIterator () yoktur , bu nedenle ArrayList'i ters yönde yinelemek için kendi kodumuzu yazmamız gerekir.


4. Başlangıç ​​Kapasitesi

Yapıcı aşırı yüklenmemişse, ArrayList başlangıç ​​kapasitesi 10'un boş bir listesini oluştururken,

LinkedList boş listeyi yalnızca başlangıç ​​kapasitesi olmadan oluşturur.


5. Bellek Yükü

LinkedList'te bellek ek yükü ArrayList ile karşılaştırıldığında daha fazladır, çünkü bir düğümün sonraki ve önceki düğümün adreslerini tutması gerekir. Süre

Gelen ArrayList her dizin sadece gerçek nesne (veri) tutar.


Kaynak


18

Bu Listede gerçekleştirdiğim işlemlerin zaman karmaşıklıklarına dayanarak genellikle diğerini kullanıyorum.

|---------------------|---------------------|--------------------|------------|
|      Operation      |     ArrayList       |     LinkedList     |   Winner   |
|---------------------|---------------------|--------------------|------------|
|     get(index)      |       O(1)          |         O(n)       | ArrayList  |
|                     |                     |  n/4 steps in avg  |            |
|---------------------|---------------------|--------------------|------------|
|      add(E)         |       O(1)          |         O(1)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     | O(n) in worst case  |                    |            |
|---------------------|---------------------|--------------------|------------|
|    add(index, E)    |       O(n)          |         O(n)       | LinkedList |
|                     |     n/2 steps       |      n/4 steps     |            |
|                     |---------------------|--------------------|            |
|                     |                     |  O(1) if index = 0 |            |
|---------------------|---------------------|--------------------|------------|
|  remove(index, E)   |       O(n)          |         O(n)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     |     n/2 steps       |      n/4 steps     |            |
|---------------------|---------------------|--------------------|------------|
|  Iterator.remove()  |       O(n)          |         O(1)       | LinkedList |
|  ListIterator.add() |                     |                    |            |
|---------------------|---------------------|--------------------|------------|


|--------------------------------------|-----------------------------------|
|              ArrayList               |            LinkedList             |
|--------------------------------------|-----------------------------------|
|     Allows fast read access          |   Retrieving element takes O(n)   |
|--------------------------------------|-----------------------------------|
|   Adding an element require shifting | o(1) [but traversing takes time]  |
|       all the later elements         |                                   |
|--------------------------------------|-----------------------------------|
|   To add more elements than capacity |
|    new array need to be allocated    |
|--------------------------------------|

ArrayDeque, dizileri biraz daha dengeler, çünkü ön / arka ekle / kaldır hepsi O (1) Bağlantılı Listenin hâlâ kazandığı tek şey (Yineleme işlemleri) ekleme / çıkarmadır.
Bill K

14

Yukarıdaki diğer iyi argümanlar ek olarak, fark etmelidir ArrayListuygular RandomAccessederken, arayüz LinkedListuygular Queue.

Böylece, bir şekilde verimlilik ve davranış farkı ile biraz farklı problemleri ele alıyorlar (bkz. Yöntem listesi).


10

Bu, Listede daha fazla hangi işlemleri yapacağınıza bağlıdır.

ArrayListdizinlenmiş bir değere erişmek daha hızlıdır. Nesneleri eklerken veya silerken çok daha kötüdür.

Daha fazla bilgi edinmek için, diziler ve bağlantılı listeler arasındaki farktan söz eden makaleleri okuyun.


2
Daha fazla bilgi edinmek için kodu yazmayın. ekleme ve silme işlemlerinde ArrayList uygulamasının LinkedList'ten daha hızlı olduğunu göreceksiniz.
kachanov

8

Dizi listesi temelde öğe vb. Ekleme yöntemlerine sahip bir dizidir (ve bunun yerine genel bir liste kullanmalısınız). Bir indeksleyici ile erişilebilen bir öğeler topluluğudur (örneğin [0]). Bir öğeden diğerine ilerleme anlamına gelir.

Bağlantılı liste, bir öğeden diğerine ilerlemeyi belirtir (Madde a -> madde b). Bir dizi listesiyle aynı efekti elde edebilirsiniz, ancak bağlantılı bir liste kesinlikle hangi öğenin öncekini izlemesi gerektiğini söyler.



7

Bağlantılı bir listenin önemli bir özelliği (başka bir cevapta okumadım) iki listenin birleştirilmesidir. Bir dizi ile bu bağlantılı bir liste ile O (n) (+ bazı yeniden tahsislerin yükü) bu sadece O (1) veya O (2) ;-)

Önemli : Java için LinkedListbu doğru değildir! Bkz Java bağlantılı liste için hızlı bir concat yöntem var mı?


2
Bu nasıl? Bu, bağlantılı liste veri yapıları için geçerli olabilir, ancak bir Java LinkList nesnesi için geçerli olmayabilir. A'yı nextbir listeden ikinci listedeki ilk düğüme işaret edemezsiniz . Tek yol, her öğeyi addAll()döngüye sokmak ve çağırmaktan daha iyi olsa da, sırayla öğeleri ekleyen kullanmaktır add(). Bunu O (1) 'de hızlı bir şekilde yapmak için bir birleştirme sınıfına (org.apache.commons.collections.collection.CompositeCollection gibi) ihtiyacınız olacak, ancak bu her türlü Liste / Koleksiyon için işe yarayacaktır.
Kevin Brock

Evet doğru. Cevabı buna göre düzenledim. ama LinkedList ile bunu nasıl yapacağınızı öğrenmek için şu cevaba bakınız: stackoverflow.com/questions/2494031/…
Karussell

7

ArrayList ve LinkedList'in kendi artıları ve eksileri vardır.

ArrayList, bir sonraki düğüme doğru işaretçiler kullanan LinkedList ile karşılaştırıldığında bitişik bellek adresi kullanır. Dolayısıyla, bir ArrayList öğesinde bir öğeyi aramak istediğinizde LinkedList ile n yineleme yapmaktan daha hızlıdır.

Öte yandan, bir LinkedList'e ekleme ve silme çok daha kolaydır, çünkü sadece işaretçileri değiştirmeniz gerekir, oysa ArrayList herhangi bir ekleme veya silme için kaydırma işlemi kullanımını gerektirir.

Uygulamanızda sık sık alım işlemleriniz varsa bir ArrayList kullanın. Sık ekleme ve silme işlemleriniz varsa LinkedList kullanın.


6

Yanıtları okudum, ancak her zaman bir ArrayList üzerinde fikirleri duymak için paylaşmak istediğim bir LinkedList kullandığım bir senaryo var:

Her zaman bir DB elde edilen veri listesini döndüren bir yöntem vardı her zaman bir LinkedList kullanın.

Benim mantığım, tam olarak kaç sonuç aldığımı bilmek imkansız olduğu için, bellek israfı olmayacak (ArrayList'te kapasite ve gerçek eleman sayısı arasındaki farkla olduğu gibi) olacak ve kapasiteyi çoğaltın.

Bir ArrayList'e göre, dizilerin çoğalmasını mümkün olduğunca en aza indirmek için en azından yapıcıyı her zaman başlangıç ​​kapasitesiyle kullanmanız gerektiğini kabul ediyorum.


5

ArrayListve LinkedListhem uygulamalar List interface hem de yöntemleri ve sonuçları neredeyse aynıdır. Bununla birlikte, aralarında ihtiyaca bağlı olarak birini diğerinden daha iyi yapan birkaç fark vardır.

ArrayList vs LinkedList

1) Search: ArrayListarama işlemi, arama işlemine kıyasla oldukça hızlıdır LinkedList. get(int index)içinde ArrayListperformansını verir O(1)iken LinkedListperformanstır O(n).

Reason: ArrayListdizideki veri yapısını dolaylı olarak kullandığı için listedeki bir öğenin aranmasını daha hızlı hale getiren, dizin tabanlı sistemi öğeler için tutar. Diğer taraftaLinkedList , bir eleman aramak için tüm elemanlar arasında geçiş yapılmasını gerektiren çift bağlantılı bir liste uygular.

2) Deletion: LinkedListkaldırma işlemi değişken O(1)performans verirken performans ArrayListverir: O(n)en kötü durumda (ilk elemanı çıkarırken) ve O(1)en iyi durumda (son elemanı çıkarırken).

Sonuç: LinkedList öğesinin silinmesi ArrayList'e göre daha hızlıdır.

Sebep: LinkedList'in her öğesi, listedeki her iki komşu öğeye işaret eden iki işaretçi (adres) tutar. Bu nedenle, kaldırma işlemi yalnızca, kaldırılacak düğümün iki komşu düğümünde (öğelerinde) işaretçi konumunda değişiklik gerektirir. ArrayList öğesindeyken, kaldırılan öğenin oluşturduğu alanı doldurmak için tüm öğelerin kaydırılması gerekir.

3) Inserts Performance: LinkedListekleme yöntemi O(1)performans verirken en kötü durumda ArrayListverir O(n). Nedeni, kaldırılması için açıklananla aynıdır.

4) eleman verilerini ve komşu düğümler için iki işaretçi Memory Overhead: ArrayListtutarken indeksleri ve eleman verilerini LinkedListkorur

bu nedenle LinkedList'te bellek tüketimi nispeten yüksektir.

Bu sınıflar arasında aşağıdaki gibi birkaç benzerlik vardır:

  • Hem ArrayList hem de LinkedList, Liste arabiriminin uygulanmasıdır.
  • Her ikisi de eleman ekleme sırasını korur, yani ArrayList ve LinkedList öğelerini görüntülerken, sonuç kümesi öğelerin Listeye eklendiği sıraya sahip olur.
  • Bu sınıfların her ikisi de senkronize değildir ve Collections.synchronizedList yöntemi kullanılarak açıkça senkronize edilebilir.
  • iteratorVe listIteratorbu sınıfların tarafından döndürülen olan fail-fast(liste yapısal yineleyici oluşturulduktan sonra kullanma dışında herhangi bir şekilde, herhangi bir zamanda değiştirilirse iterator’skendi kaldır veya ekleme yöntemleri, yineleyici olacak throwbir ConcurrentModificationException).

LinkedList ne zaman ve ArrayList ne zaman kullanılır?

  • İnsert ve kaldır operasyonlar Yukarıda açıklandığı gibi iyi bir performans vermek (O(1))içinde LinkedListkarşılaştırıldığındaArrayList(O(n)) .

    Bu nedenle, uygulamada sık sık ekleme ve silme gereksinimi varsa LinkedList en iyi seçimdir.

  • Search ( get method) işlemleri hızlı, Arraylist (O(1))ancak değilLinkedList (O(n))

    bu nedenle, daha az ekleme ve kaldırma işlemi ve daha fazla arama işlemi gereksinimi varsa, ArrayList sizin için en iyi seçenektir.


5

ArrayList içindeki get (i) işlemi LinkedList'ten daha hızlıdır, çünkü:
ArrayList: LinkedList Liste arayüzünün yeniden boyutlandırılabilir dizi uygulaması
: boyutlandırılabilir List ve Deque arabirimlerinin çift bağlantılı listesi uygulaması

Listede dizin oluşturan işlemler, belirtilen dizine hangisi daha yakınsa listeyi baştan veya sondan itibaren değiştirir.


5

1) Temel Veri Yapısı

ArrayList ve LinkedList arasındaki ilk fark, ArrayList'in Array tarafından desteklenirken, LinkedList LinkedList tarafından desteklenir. Bu, performansta daha fazla farklılığa yol açacaktır.

2) LinkedList Deque uygular

ArrayList ve LinkedList arasındaki bir diğer fark, LinkedList, List arabirimi dışında, add () ve poll () ve diğer birkaç Deque işlevi için ilk giren ilk işlemleri sağlayan Deque arabirimini de uygular. 3) ArrayList öğelerine öğe ekleme ArrayList öğesinde öğe ekleme, Array öğesinin yeniden boyutunu tetiklemezse O (1) işlemidir, bu durumda O (log (n)) olur, Öte yandan, LinkedList, herhangi bir navigasyon gerektirmediği için O (1) işlemidir.

4) Bir elemanın bir konumdan çıkarılması

Belirli bir dizinden bir öğeyi kaldırmak için örneğin remove (index) öğesini çağırmak üzere, ArrayList, O (n) 'ye yakın hale getiren bir kopyalama işlemi gerçekleştirirken, LinkedList'in de O (n / 2) yapan o noktaya geçmesi gerekir. , yakınlığa dayalı olarak her iki yönden de hareket edebildiği için.

5) ArrayList veya LinkedList üzerinden yineleme

Yineleme, LinkedList ve ArrayList için O (n) işlemidir; burada n, bir dizi öğedir.

6) Elemanın bir konumdan alınması

Get (index) işlemi ArrayList'te O (1) iken LinkedList'teki O (n / 2), bu girişe kadar geçiş yapması gerekir. Yine de, Büyük O gösterimlerinde O (n / 2) sadece O (n) 'dir çünkü orada sabitleri görmezden geliriz.

7) Bellek

LinkedList, veriyi saklamak için statik iç içe bir sınıf ve sonraki ve önceki iki düğüm olan bir sargı nesnesi olan Entry'yi kullanırken ArrayList, verileri yalnızca Array'da depolar.

Bu nedenle, ArrayList durumunda ArrayList söz konusu olduğunda, Array öğesinin içeriği bir Array öğesinden diğerine kopyalarken yeniden boyutlandırma işlemini gerçekleştirmesi durumu haricinde, bellek gereksinimi LinkedList'ten daha az görünür.

Dizi yeterince büyükse, bu noktada çok fazla bellek alabilir ve Çöp toplamayı tetikleyebilir, bu da yanıt süresini yavaşlatabilir.

ArrayList ile LinkedList arasındaki yukarıdaki tüm farklardan, ArrayList, remove () veya get () 'den daha sık bir add () işlemi yapmanız dışında hemen hemen tüm durumlarda LinkedList'ten daha iyi bir seçimdir.

Bağlantılı listeyi ArrayList'e göre değiştirmek daha kolaydır, özellikle de bağlantılı listeyi başlangıç ​​veya bitişten ekliyorsanız veya kaldırıyorsanız, bağlantılı liste dahili olarak bu konumların referanslarını tutar ve bunlara O (1) zamanda erişilebilir.

Başka bir deyişle, eleman eklemek istediğiniz konuma ulaşmak için bağlantılı listede gezinmenize gerek yoktur, bu durumda ekleme O (n) işlemi haline gelir. Örneğin, bağlı listenin ortasına bir öğe ekleme veya silme.

Bence Java'daki pratik amaçların çoğu için LinkedList üzerinden ArrayList kullanın.


1
Bence bu, tüm grubun en iyi ifade edilen cevabı. Doğru ve bilgilendirici. Son satırın değiştirilmesini öneririm - sonunda "sıralardan bir kenara" ekleyin, bu gerçekten bağlantılı bir liste için hiç mantıklı olmayan çok önemli yapılardır.
Bill K

3

Burada gördüğüm testlerden biri testi sadece bir kez yapıyor. Ama farkettim ki, bu testleri birçok kez çalıştırmanız gerekiyor ve sonunda zamanları birleşecek. Temel olarak JVM'nin ısınması gerekir. Özel kullanım durumum için, yaklaşık 500 öğeye kadar büyüyen bir listeye öğe eklemem / kaldırmam gerekiyordu. Testlerim LinkedListdaha hızlı çıktı, LinkedListyaklaşık 50.000 NS ArrayListgeliyor ve yaklaşık 90.000 NS geliyor ... ver ya da al. Aşağıdaki koda bakınız.

public static void main(String[] args) {
    List<Long> times = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        times.add(doIt());
    }
    System.out.println("avg = " + (times.stream().mapToLong(x -> x).average()));
}

static long doIt() {
    long start = System.nanoTime();
    List<Object> list = new LinkedList<>();
    //uncomment line below to test with ArrayList
    //list = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        list.add(i);
    }

    Iterator it = list.iterator();
    while (it.hasNext()) {
        it.next();
        it.remove();
    }
    long end = System.nanoTime();
    long diff = end - start;
    //uncomment to see the JVM warmup and get faster for the first few iterations
    //System.out.println(diff)
    return diff;
}

2

Remove () ve insert () öğelerinin hem ArrayLists hem de LinkedLists için O (n) çalışma zamanı verimliliği vardır. Bununla birlikte, doğrusal işlem süresinin arkasındaki neden çok farklı iki nedenden kaynaklanmaktadır:

Bir ArrayList'te O (1) öğesindeki öğeye ulaşırsınız, ancak aslında bir şeyin kaldırılması veya eklenmesi onu aşağıdaki öğelerin O (n) yapar çünkü aşağıdaki tüm öğelerin değiştirilmesi gerekir.

LinkedList'te, istenen öğeye ulaşmak O (n) gerektirir, çünkü istenen dizine ulaşana kadar en baştan başlamalıyız. Aslında kaldırma veya ekleme sabittir, çünkü remove () için yalnızca 1 referansı ve insert () için 2 referansını değiştirmemiz gerekir.

İkisinin hangisi ekleme ve çıkarma için daha hızlı olduğu, nerede olduğuna bağlıdır. Eğer başlangıca yaklaşırsak LinkedList daha hızlı olacaktır, çünkü nispeten az sayıda öğeden geçmemiz gerekir. Eğer sona yaklaşırsak, bir ArrayList daha hızlı olacaktır, çünkü oraya sabit zamanda ulaşırız ve sadece onu takip eden kalan birkaç öğeyi değiştirmeliyiz. Tam olarak ortada yapıldığında LinkedList daha hızlı olacaktır çünkü n öğesinden geçmek n değerlerini taşımaktan daha hızlıdır.

Bonus: Bir ArrayList için bu iki yöntemi O (1) yapmanın bir yolu olmasa da, aslında LinkedLists'te bunu yapmanın bir yolu vardır. Diyelim ki yolumuzdaki öğeleri kaldırarak ve ekleyerek tüm Listeyi gözden geçirmek istiyoruz. Genellikle, LinkedList'i kullanarak her öğe için en baştan başlarsınız, aynı zamanda bir Yineleyici ile üzerinde çalıştığımız mevcut öğeyi "kaydedebiliriz". Yineleyicinin yardımıyla, LinkedList'te çalışırken remove () ve insert () için O (1) verimliliği elde ederiz. Bir LinkedList'in her zaman bir ArrayList'ten daha iyi olduğunun farkında olduğum tek performans avantajı yapmak.


1

ArrayList, AbstractList'i genişletir ve Liste Arayüzünü uygular. ArrayList dinamik dizidir.
Temel olarak dizilerin dezavantajlarının üstesinden gelmek için oluşturulduğu söylenebilir

LinkedList sınıfı AbstractSequentialList'i genişletir ve List, Deque ve Queue arabirimini uygular.
Performans
arraylist.get()O (1) iken linkedlist.get()O (n)
arraylist.add()O (1) ve linkedlist.add()0 (1)
arraylist.contains()O (n) ve linkedlist.contains()O (n)
arraylist.next()O (1) ve linkedlist.next()O (1)
arraylist.remove()O (n) 'dir oysa linkedlist.remove()O (1) olup
ArrayList
iterator.remove()O (n)
olarak LinkedList ise
iterator.remove()O'dur (1)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.