.NET veri yapıları: ArrayList, List, HashTable, Dictionary, SortedList, SortedDictionary - Hız, bellek ve her biri ne zaman kullanılır?


213

.NET'in birçok karmaşık veri yapısı vardır. Ne yazık ki, bazıları oldukça benzer ve ne zaman ve ne zaman kullanılacağından her zaman emin değilim. C # ve Visual Basic kitaplarımın çoğu onlar hakkında bir dereceye kadar konuşuyor, ancak hiçbir zaman gerçek bir ayrıntıya girmiyorlar.

Array, ArrayList, List, Hashtable, Dictionary, SortedList ve SortedDictionary arasındaki fark nedir?

Hangileri numaralandırılabilir (IList - 'foreach' döngüleri yapabilir)? Hangileri anahtar / değer çiftlerini (Kimlik) kullanır?

Bellek ayak izi ne olacak? Yerleştirme hızı? Alma hızı?

Bahsetmeye değer başka veri yapıları var mı?

Hala bellek kullanımı ve hızı (Big-O notasyonu) hakkında daha fazla ayrıntı arıyorum.


12
Bu soruyu parçalara ayırmalısın. Yarısı basit bir Google aramasının cevaplayabileceği yirmi farklı şey soruyorsunuz. Lütfen daha spesifik ol; sorunuz çok dağınık olduğunda yardım etmek zor.

33
Ayrılmayı düşündüm, ancak birisinin tüm bu cevapları tek bir yerde birleştirebileceğini fark ettim. Aslında, birisi her şeyi profillendiren bir tablo bulabilirse, bu sitede harika bir kaynak olabilir.
Pretzel

9
Bu soru bir wikiye dönüştürülebilir mi?
BozoJoe

1
Bu MSDN makalesi, ağaçlar, grafikler ve kümeler, Veri Yapılarının Kapsamlı Bir İncelemesi
Ryan Fisher

1
Ryan, bu bağlantıdaki makaleler 14 yaşında (yazı sırasında 12). Yan notu geçen hafta kendim okuyorum. ancak daha yeni teknolojiler içermezler ve umutsuzca güncellenmeleri gerekir. Daha fazla performans metriği ve örneği.
htm11h

Yanıtlar:


156

Kafamın üstünden:

  • Array* - eski bir bellek dizisini temsil eder - normal bir type[]dizi için bir takma ad gibi . Numaralandırabilir. Otomatik olarak büyüyemez. Çok hızlı kesici uç ve retrival hızını kabul ediyorum.

  • ArrayList- otomatik olarak büyüyen dizi. Daha fazla ek yük ekler. Enum., Muhtemelen normal bir diziden daha yavaş ama yine de oldukça hızlı. Bunlar .NET'te çok kullanılır

  • List- benim favs biri - jenerikler ile kullanılabilir, böylece kuvvetle yazılan bir dizi olabilir, örneğin List<string>. Bunun dışında çok benzerArrayList

  • Hashtable- sade eski hashtable. O (1) ila O (n) arasındaki en kötü durum. Değer ve anahtar özelliklerini numaralandırabilir ve anahtar / val çiftleri yapabilir

  • Dictionary - yukarıdaki gibi sadece jenerikler yoluyla güçlü bir şekilde yazılmıştır. Dictionary<string, string>

  • SortedList- sıralanmış bir genel liste. Bir şeyleri nereye koyacağınızı anlaması gerektiğinden yerleştirme işlemi yavaşladı. Numaralandırma olabilir, başvurmak zorunda olmadığından muhtemelen almada aynıdır, ancak silme düz eski bir listeden daha yavaş olacaktır.

Ben kullanma eğiliminde Listve Dictionaryher zaman - Bunları kuvvetle jenerik ile yazılan kullanmaya başladığınızda, onun gerçekten zor standart dışı jenerik olanları geri dönmek için.

Çok sayıda başka veri yapısı da var - KeyValuePairbazı ilginç şeyler yapmak için kullanabileceğiniz şey var, bir SortedDictionaryde yararlı olabilir.


3
Hash Tablosu O (1), en kötü durum (çarpışmalarla) O (n) olabilir
Justin Bozonier

7
Buraya eklemeniz gereken başka birçok veri yapısı vardır. LinkedList, Listeyi Atla, Yığın, Sıra, Öbek, Ağaçlar, Grafikler gibi. Bunlar da çok önemli veri yapılarıdır.
DarthVader

2
.Net 4.0'a eklenen ConcurrentDictionary, Thread Safety
Harindaka

2
Ayrıca BlockingCollection <T>, iş parçacığı için güvenli bir üretici / tüketici uygulaması sağlar
Harindaka

7
ArrayListsanal yöntemler kullanır, ancak List<T>kullanmaz. standart koleksiyonlar için ve özel koleksiyonlar için temel sınıf olarak ArrayListyerini almıştır . yerini büyük ölçüde almıştır . Yeni kod için kaçınmak ve tavsiye ederim . List<T>Collection<T>HashtableDictionary<TKey, TValue>ArrayListHashtable
Sam Harwell

29

Mümkünse jenerik ilaç kullanın. Bu içerir:

  • ArrayList yerine liste
  • HashTable yerine sözlük

24

İlk olarak, .NET'teki tüm koleksiyonlar IEnumerable uygular.

İkinci olarak, koleksiyonların çoğu kopyadır, çünkü jenerikler çerçevenin 2.0 sürümünde eklenmiştir.

Bu nedenle, genel koleksiyonlar büyük olasılıkla özellikler eklese de, çoğunlukla:

  • Liste, ArrayList'in genel bir uygulamasıdır.
  • Sözlük Hashtable'ın genel bir uygulamasıdır

Diziler, belirli bir dizinde depolanan değeri değiştirebileceğiniz sabit boyutlu bir koleksiyondur.

SortedDictionary, anahtarlara göre sıralanan bir IDictionary. SortedList, gerekli bir IComparer'a göre sıralanan bir IDictionary.

Yani, IDictionary uygulamaları (KeyValuePairs destekleyenler) şunlardır: * Hashtable * Sözlük * SortedList * SortedDictionary

.NET 3.5'te eklenen başka bir koleksiyon Hashset'tir. Ayarlanan işlemleri destekleyen bir koleksiyon.

Ayrıca, LinkedList standart bir bağlantılı liste uygulamasıdır (Liste daha hızlı erişim için bir dizi listesidir).


20

İşte size birkaç genel ipucu:

  • foreachUygulanan türlerde kullanabilirsiniz IEnumerable. IListesas olarak bir edilir IEnumberableile Countve Itemözellikleri, (a sıfır tabanlı dizin kullanarak öğeleri erişim). IDictionaryÖte yandan, herhangi bir yıkanabilir dizine göre öğelere erişebileceğiniz anlamına gelir.

  • Array, ArrayListVe Listtüm uygulamak IList. Dictionary, SortedDictionaryve Hashtableuygulayın IDictionary.

  • .NET 2.0 veya üstünü kullanıyorsanız, belirtilen türlerin genel meslektaşlarını kullanmanız önerilir.

  • Bu türler üzerindeki çeşitli işlemlerin zaman ve mekan karmaşıklığı için belgelerine başvurmalısınız.

  • .NET veri yapıları System.Collectionsad alanındadır. Ek veri yapıları sunan PowerCollections gibi tür kitaplıkları vardır .

  • Veri yapılarını tam olarak anlamak için CLRS gibi kaynaklara danışın .


1
dan msdn değil ılist - IDictionnary uygulamak SortedList gibi görünüyor
Haim Bendanan

Sabit. yorum için teşekkürler. SortedList, anahtar / değerlerin bir listesini tutar, bu nedenle temelde bir sözlüğün verilerini temsil eder. Cevabı ilk yazdığımda bu sınıfın nasıl çalıştığını hatırlamıyorum ...
blackwing

9

.NET veri yapıları:

ArrayList ve List'in neden gerçekten farklı olduğu hakkında daha fazla bilgi

Diziler

Bir kullanıcının belirttiği gibi, Diziler "eski okul" koleksiyonudur (evet, diziler bir parçası olmasa da bir koleksiyon olarak kabul edilir System.Collections). Ancak, diğer koleksiyonlara kıyasla dizilerle ilgili "eski okul" nedir, yani başlığınızda listeledikleriniz (burada, ArrayList ve List (Of T))? Dizilere bakarak temel bilgilerle başlayalım.

Başlamak için, Microsoft .NET'teki Diziler "birkaç mantıksal ilişkili öğeyi tek bir koleksiyon olarak ele almanızı sağlayan mekanizmalardır" (bağlantılı makaleye bakın). Bu ne anlama geliyor? Diziler, ayrı ayrı üyeleri (elemanları) bir başlangıç ​​adresiyle birbiri ardına bellekte depolar. Diziyi kullanarak, o adresten başlayarak sıralı olarak saklanan öğelere kolayca erişebiliriz.

Bunun ötesinde ve 101 ortak anlayışı programlamanın aksine, Diziler gerçekten oldukça karmaşık olabilir:

Diziler tek boyutlu, çok boyutlu veya yorgun olabilir (pürüzlü diziler okunmaya değer). Dizilerin kendisi dinamik değildir: bir kez başlatıldığında, n boyutundaki bir dizi n sayıda nesneyi tutmak için yeterli alan ayırır . Dizideki öğelerin sayısı büyüyemez veya küçülemez. Dim _array As Int32() = New Int32(100)dizinin bellek bloğunda 100 Int32 ilkel tür nesnesi içermesi için yeterli alan ayırır (bu durumda, dizi 0'ları içerecek şekilde başlatılır). Bu bloğun adresi adresine geri döner _array.

Makaleye göre, Ortak Dil Belirtimi (CLS) tüm dizilerin sıfır temelli olmasını gerektirir. .NET'teki diziler sıfır tabanlı olmayan dizileri destekler; ancak, bu daha az yaygındır. Sıfır tabanlı dizilerin "yaygınlığı" sonucunda Microsoft, performanslarını optimize etmek için çok zaman ; bu nedenle, tek boyutlu, sıfır tabanlı (SZ'ler) diziler "özel" dir - ve gerçekten de bir dizinin en iyi uygulamasıdır (çok boyutlu vb. aksine) - çünkü SZ'lerin onları işlemek için belirli ara dil talimatları vardır.

Diziler her zaman referans olarak geçirilir (bir bellek adresi olarak) - bilinmesi gereken Array bulmacasının önemli bir parçası. Sınır denetimi yaparken (bir hata verir), sınır denetimi de dizilerde devre dışı bırakılabilir.

Yine, dizilerin önündeki en büyük engel yeniden boyutlandırılmamalarıdır. "Sabit" kapasiteye sahiptirler. ArrayList ve List'in (Of T) tarihimize tanıtılması:

ArrayList - genel olmayan liste

ArrayList (birlikte List(Of T)- bazı kritik farklılıklar vardır gerçi, burada, daha sonra anlatılacak) - belki (geniş anlamda) koleksiyonlarının yanında ek olarak en iyi düşüncedir. ArrayList, IList ('ICollection' ın soyundan) arayüzünden miras alır . ArrayLists, kendileri Listelerden daha hantaldır - daha fazla ek yük gerektirirler .

IListuygulamanın ArrayLists'e sabit boyutlu listeler (Arrays gibi) gibi davranmasını sağlar; ancak, ArrayLists tarafından eklenen ek işlevsellik ötesinde, bu durumda ArrayLists (Diziler üzerinde) olarak sabit boyutlu ArrayLists kullanmanın belirgin şekilde daha yavaş olmasının gerçek bir avantajı yoktur.

Okuduğumdan, ArrayLists tırtıklı olamaz: "Çok boyutlu dizileri eleman olarak kullanmak ... desteklenmiyor". Yine, ArrayLists tabutundaki başka bir çivi. Dizi Listeleri da "Yazılan" değildir - yani altına her şey, bir ArrayList nesneleri dinamik Dizisi basitçe: Object[]. Bu, ArrayLists'i uygularken, yine ek yüklerine ekleyerek çok fazla boks (örtük) ve unboxing (açık) gerektirir.

Asılsız düşünce: Sanırım profesörlerimden birini okuduğumu veya duyduğumu, ArrayList'lerin Dizilerden Liste tipi Koleksiyonlara geçme girişiminin piç kavramsal çocuğu olduğunu hatırlıyorum, yani bir kez Diziler için büyük bir gelişme olurken, koleksiyonlarla ilgili daha fazla geliştirme yapıldığı için artık en iyi seçenek değiller

Liste (Of T): ArrayList'in ne olduğu (ve olmasını umduğu)

Bellek kullanımındaki fark, bir Listenin (Int32) aynı ilkel tür içeren bir ArrayList'ten% 56 daha az bellek tükettiği kadar önemlidir (yukarıdaki beyefendinin bağlantılı gösterisinde 8 MB'ye karşı 19 MB: yine buraya bağlanmıştır ) - yine de bu 64-bit makine tarafından birleştirilen bir sonuçtur. Bu fark gerçekten iki şeyi gösterir: birincisi (1), kutulu bir Int32 tipi "nesne" (ArrayList) saf bir Int32 ilkel tipinden (Liste) çok daha büyüktür; ikincisi (2), 64-bitlik bir makinenin iç işleyişinin bir sonucu olarak fark üsteldir.

Peki, fark nedir ve bir liste nedir (Of T) ? MSDN , bir List(Of T)", dizin tarafından erişilebilen, güçlü bir şekilde yazılmış nesnelerin listesini" olarak tanımlar . Burada önemli olan "kuvvetle yazılan" bittir: bir List (Of T) 'tipleri' tanır 've nesneleri kendi tipleri olarak saklar. Yani, an bir tür Int32olarak Int32değil , bir olarak depolanır Object. Bu, boks ve kutudan çıkarmanın neden olduğu sorunları ortadan kaldırır.

MSDN, bu farkın yalnızca ilkel türleri saklarken ve referans türlerini saklarken devreye girdiğini belirtir. Çok, fark gerçekten büyük ölçekte gerçekleşir: 500'den fazla eleman. Daha da ilginç olanı, MSDN belgelerinin "ArrayList sınıfını kullanmak yerine List (Of T) sınıfının türe özgü uygulamasını kullanmanızdır."

Esasen, List (Of T) ArrayList'tir, ancak daha iyidir. ArrayList'in "genel eşdeğeridir". ArrayList gibi, sıralanana kadar sıralanması garanti edilmez (şekil). List (Of T) 'nin bazı ek işlevleri de vardır.


5

Soruya sempati duyuyorum - ben de (bul?) Seçim şaşkınlığı buldum, bu yüzden hangi veri yapısının en hızlı olduğunu görmek için bilimsel olarak yola çıktım (VB kullanarak testi yaptım, ancak C # 'nin aynı olacağını hayal ediyorum, çünkü her iki dil aynı şeyi CLR düzeyinde yapın). Burada benim tarafımdan yapılan bazı karşılaştırma sonuçlarını görebilirsiniz (ayrıca hangi durumlarda hangi veri türünün en iyi şekilde kullanılacağına dair tartışmalar da vardır).


3

Onlar zekayla gayet iyi hecelendiler. Sadece System.Collections yazın. veya System.Collections.Generics (tercih edilir) ve mevcut olanların bir listesini ve kısa açıklamasını alırsınız.


3

Hashtables / Sözlükler O (1) performansıdır, yani performans bir boyut işlevi değildir. Bunu bilmek önemlidir.

EDIT: Uygulamada, Hashtable / Sözlük <> aramalar için ortalama zaman karmaşıklığı O (1) 'dir.


5
"Performans" diye bir şey yoktur. Karmaşıklık operasyona bağlıdır. Örneğin, Sözlük <> öğesine n öğe eklerseniz, rehashing nedeniyle O (1) olmaz.
Ilya Ryzhenkov

2
FYI, rehashing ile bile, Sözlük hala O (1). Sözlüğü, Sözlük genişlemeden hemen önce düşünün. Elementlerin yarısı - son genişlemeden bu yana eklenenler - bir kez karma hale getirilecek. Kalanın yarısı iki kez haşlanmış olacak. Bundan geriye kalanların yarısı, üç kez, vb. Her bir eleman üzerinde ortalama ortalama hash işlemi sayısı 1 + 1/2 + 1/4 + 1/8 ... = 2 olacaktır. Genişlemeden hemen sonra durum esasen aynıdır, ancak her eleman bir kez daha hash edilmişse (ortalama karma sayısı üçtür). Diğer tüm senaryolar bunların arasındadır.
supercat

3

Genel koleksiyonlar, özellikle birçok öğeyi yinelediğinde, genel olmayan karşılıklarına göre daha iyi performans gösterir. Boks ve kutudan çıkarma artık gerçekleşmez çünkü.


2

Yüksek frekanslı sistematik ticaret mühendisliği için Hashtable vs Dictionary hakkında önemli bir not: İş Parçacığı Güvenliği Sorunu

Hashtable, birden çok iş parçacığı tarafından kullanılmak üzere iş parçacığı için güvenlidir. Sözlük genel statik üyeleri iş parçacığı için güvenlidir, ancak herhangi bir örnek üyesinin böyle olacağı garanti edilmez.

Dolayısıyla Hashtable bu konuda 'standart' seçenek olmaya devam ediyor.


Bu kısmen doğrudur. Aynı Hashtableanda yalnızca bir yazar ve birden fazla okuyucu ile kullanmak güvenlidir. Diğer yandan, Dictionaryeşzamanlı olarak değiştirilmediği sürece, birden fazla okuyucu ile kullanmak güvenlidir .
Bryan Menard

Kesinlikle. Bununla birlikte, ticaret alanında, aynı anda canlı piyasa verilerinden okuyoruz ve ekli girişleri içeren analitikler çalıştırıyoruz. Ayrıca, kaç tüccarın sistemi kullandığına da bağlıdır - eğer sadece sizseniz, önemli değil.
Rob

1
.NET 4.0 bir ConcurrentDictionary <TKey, TValue> sağlar
Rob

1

Jenerik ve jenerik olmayan koleksiyonlar arasında ince ve çok ince olmayan farklılıklar vardır. Sadece altta yatan farklı veri yapıları kullanırlar. Örneğin, Hashtable senkronize etmeden bir yazar-çok-okuyucuları garanti eder. Sözlük yok.


1

En popüler C # Veri Yapıları ve Koleksiyonları

  • Dizi
  • ArrayList
  • Liste
  • Bağlantılı liste
  • Sözlük
  • HashSet
  • yığın
  • kuyruk
  • SortedList

C # .NET birçok farklı veri yapısına sahiptir, örneğin, en yaygın olanlarından biri bir Array'dir. Ancak C # daha birçok temel veri yapısı ile birlikte gelir. Kullanılacak doğru veri yapısını seçmek, iyi yapılandırılmış ve verimli bir program yazmanın bir parçasıdır.

Bu makalede, C # .NET 3.5'te tanıtılan yenileri de dahil olmak üzere yerleşik C # veri yapılarının üzerinden geçeceğim. Bu veri yapılarının çoğunun diğer programlama dilleri için geçerli olduğunu unutmayın.

Dizi

Belki de en basit ve en yaygın veri yapısı dizidir. AC # dizisi temelde nesnelerin listesidir. Tanımlayıcı özellikleri, tüm nesnelerin aynı tipte olması (çoğu durumda) ve belirli bir sayıları olmasıdır. Bir dizinin doğası, listedeki konumlarına (dizin olarak da bilinir) göre öğelere çok hızlı erişim sağlar. AC # dizisi şu şekilde tanımlanır:

[object type][] myArray = new [object type][number of elements]

Bazı örnekler:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

Yukarıdaki örnekte de görebileceğiniz gibi, bir dizi elemansız veya mevcut bir değer kümesinden başlatılabilir. Değerleri bir diziye eklemek, uygun oldukları sürece basittir. Dizinin boyutundan daha fazla öğe olduğunda işlem pahalı hale gelir ve bu noktada dizinin genişletilmesi gerekir. Varolan tüm öğelerin yeni, daha büyük diziye kopyalanması gerektiğinden bu daha uzun sürer.

ArrayList

C # veri yapısı ArrayList dinamik bir dizidir. Bunun anlamı, bir ArrayList öğesinin herhangi bir miktarda nesneye ve herhangi bir türe sahip olabilmesidir. Bu veri yapısı, bir diziye yeni eleman ekleme işlemlerini basitleştirmek için tasarlanmıştır. Kaputun altında, bir ArrayList, alanı her tükendiğinde boyutu iki katına çıkan bir dizidir. Dahili dizinin iki katına çıkarılması, uzun vadede öğe kopyalama miktarını azaltan çok etkili bir stratejidir. Burada bunun ispatına girmeyeceğiz. Veri yapısının kullanımı çok basittir:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

ArrayList veri yapısının dezavantajı, alınan değerleri orijinal türlerine geri döndürmek zorundadır:

int arrayListValue = (int)myArrayList[0]

Kaynaklar ve daha fazla bilgiyi burada bulabilirsiniz :


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.