Dizi ve Liste <T>: Hangisi ne zaman kullanılır?


593
MyClass[] array;
List<MyClass> list;

Biri diğerine tercih edildiğinde senaryolar nelerdir? Ve neden?


9
Diziler, burada popüler bir tartışmada görüldüğü gibi oldukça eski . Ayrıca burada ve blogda bizim ev sahibi tarafından dikkat çekti .
gimel

9
Yanılmıyorsam Liste <> 'nin iç yapı olarak bir dizisi vardır. Dahili dizi her doldurulduğunda, içeriği yalnızca boyutun iki katı olan (veya geçerli boyutun sabit katlarından başka bir dizi) bir diziye kopyalar. en.wikipedia.org/wiki/Dynamic_array
Ykok

Ykok: Söylediğiniz doğru görünüyor, List <> ' nin kaynak kodunu burada buldum .
Carol

19
@gimel Dizilerin kullanılmadığını savunmak belki biraz cesur
awdz9nld

Yanıtlar:


580

Gerçekte, bir dizi kullanmak istemeniz nadirdir. List<T>Dizileri yeniden boyutlandırmak pahalı olduğundan, kesinlikle veri eklemek / kaldırmak istediğiniz herhangi bir zamanda kullanın . Verilerin sabit uzunlukta olduğunu biliyorsanız ve çok özel bir nedenden dolayı (kıyaslamadan sonra) mikro optimizasyon yapmak istiyorsanız , bir dizi yararlı olabilir.

List<T>bir diziden çok daha fazla işlevsellik sunar (LINQ onu biraz geliştirse de) ve neredeyse her zaman doğru seçimdir. paramsElbette argümanlar hariç . ;-p

Bir sayaç olarak - List<T>tek boyutludur; nerede-gibi dikdörtgen (vb.) dizileriniz olduğu gibi - int[,]veya string[,,]bir nesne modelinde bu tür verileri (gerekirse) modellemenin başka yolları da vardır.

Ayrıca bakınız:

Bununla birlikte, protobuf-net projemde çok fazla dizi kullanıyorum ; tamamen performans için:

  • çok fazla bit kaydırma yapar, bu nedenle byte[]kodlama için oldukça önemlidir;
  • byte[]Temel akışa (ve vv) göndermeden önce doldurduğum yerel bir yuvarlanma tamponu kullanıyorum ; BufferedStreamvs daha hızlı ;
  • boyut bir kez oluşturulduktan sonra sabitlendiğinden ve çok hızlı olması gerektiğinden, dahili olarak dizi tabanlı bir nesne modeli ( Foo[]yerine List<Foo>) kullanır .

Ancak bu kesinlikle bir istisnadır; genel iş kolu işleme için List<T>her seferinde kazanır.


8
Yeniden boyutlandırma argümanı tamamen geçerlidir. Ancak insanlar yeniden boyutlandırma gerekmediğinde bile Listeleri tercih ederler. Bu ikinci durum için, sağlam, mantıklı bir argüman var mı veya "diziler modası geçmedi" den başka bir şey değil mi?
Frederick The Fool

6
"Verileri eklemek / kaldırmak istediğinizde kesinlikle bir Liste <T> kullanın, çünkü dizileri yeniden boyutlandırmak pahalıdır." <T> listesi dahili olarak bir dizi kullanır. LinkedList <T>'i mi düşünüyordunuz?
dan-gph

14
Daha fazla özellik == daha karmaşık == bu özelliklere ihtiyacınız yoksa iyi değil. Bu cevap temel olarak dizilerin daha iyi olmasının nedenlerini listeler, ancak tam tersi bir sonuç çıkarır.
Eamon Nerbonne

12
Eğer bu özellikleri kullanmıyorsanız @EamonNerbonne, hemen hemen garanti size zarar edecek ... ama değil ki: mutasyonu gerek kalmaz koleksiyonları sayısını edilir çok daha küçük, benim deneyim, olanları daha mutasyona uğramış
Marc Gravell

7
@MarcGravell: kodlama stilinize bağlıdır. Deneyimlerime göre neredeyse hiçbir koleksiyon mutasyona uğramaz. Yani; koleksiyonlar veritabanından alınır veya bazı kaynaklardan oluşturulur, ancak daha sonraki işlemler her zaman yeni bir koleksiyon (örn. harita / filtre vb.) yeniden oluşturularak yapılır. Kavramsal mutasyonun gerekli olduğu durumlarda bile, sadece yeni bir koleksiyon oluşturmak en basit olanıdır. Bir koleksiyonu yalnızca performans optimizasyonu olarak değiştirdim ve bu tür optimizasyonlar oldukça yerel olma eğilimindedir ve mutasyonu API tüketicilerine göstermez.
Eamon Nerbonne

121

Gerçekten sadece şaşırdım bir bağlantı eklemek için cevap henüz belirtilmedi: Eric's Lippert'in "Diziler biraz zararlı olarak kabul edildi " adlı blog girişi .

Koleksiyonların pratik olduğu her yerde kullanılmasını önerdiği başlıktan karar verebilirsiniz - ancak Marc'ın haklı olarak işaret ettiği gibi, bir dizinin gerçekten tek pratik çözüm olduğu birçok yer vardır.


2
Sonunda bunu 3 yıl sonra okumaya geldim haha. O zaman iyi makale, şimdi iyi makale. :)
Spencer Ruport

21

Önerilen diğer yanıtlara bakılmaksızın, List<T>işlem yaparken diziler kullanmak isteyeceksiniz:

  • görüntü bitmap verileri
  • diğer düşük seviyeli veri yapıları (ör. ağ protokolleri)

1
Neden ağ protokolleri için? Burada özel yapıları kullanmak ve onlara özel bir serileştirici veya açık bir bellek düzeni vermek istemez misiniz? Dahası, List<T>bayt dizisi yerine burada kullanmaktan ne bahseder ?
Konrad Rudolph

8
@Konrad - iyi, yeni başlayanlar için, Stream.Read ve Stream.Write, bayt [] ile çalışır, Kodlama vb ...
Marc Gravell

12

Performansla gerçekten ilgilenmiyorsanız ve bununla birlikte, "Neden C ++ yerine .Net kullanıyorsunuz?" Liste <> ile devam etmelisiniz. Bakımı daha kolaydır ve sizin için perde arkasında bir dizi yeniden boyutlandırma tüm kirli iş yapar. (Gerekirse, Liste <> dizi boyutlarını seçme konusunda oldukça zekidir, bu nedenle genellikle gerek yoktur.)


15
"Neden C ++ yerine .Net kullanıyorsunuz?" XNA
Bengt

6

Diziler should koleksiyonu kendisinin değişmezlik istemcisi & sağlayıcı kodu (koleksiyon içindeki öğeler mutlaka değişmezlik) VE IEnumerable uygun olmadığında arasındaki sözleşmenin parçası olduğunda Listesine tercihi kullanılacak.

Örneğin,

var str = "This is a string";
var strChars = str.ToCharArray();  // returns array

"StrChars" modifikasyonunun orijinal "str" ​​nesnesini değiştirmeyeceği açıktır, "str" ​​nin altında yatan türden bağımsız olarak uygulama düzeyinde bilgi.

Ama varsayalım ki

var str = "This is a string";
var strChars = str.ToCharList();  // returns List<char>
strChars.Insert(0, 'X');

Bu durumda, insert yönteminin orijinal "str" ​​nesnesini değiştirip değiştirmeyeceği tek başına bu kod snippet'inden anlaşılamaz. Sözleşme ile Tasarım yaklaşımını kıran bu belirlemeyi yapmak için String'in uygulama düzeyinde bilgisini gerektirir. String durumunda, bu büyük bir anlaşma değil, ancak hemen hemen her durumda büyük bir anlaşma olabilir. Listeyi salt okunur olarak ayarlamak yardımcı olur, ancak derleme zamanı değil çalışma zamanı hatalarıyla sonuçlanır.


C # için nispeten yeniyim ama bir liste döndürmenin neden bir dizinin döndürülmeyeceği şekilde orijinal verilerin değişebilirliğini önerebileceği net değil. Gerçi adı ile başlayan bir yöntem, geçerli durumda orijinal nesneyi değiştirebildiğinizi önerdiğinden farklı Toolarak, orijinal örneği değiştirme yeteneği olmayan bir nesne oluşturacaktır strChars as char[].
Tim MB

@TimMB Koleksiyonun değişmezliği (öğe ekleyemez veya uzak tutamaz) ve koleksiyondaki öğelerin değişmezliği vardır. Ben ikincisine atıfta bulunuyordum, oysa muhtemelen ikisini karıştırıyorsunuz. Dizi döndürmek istemciye öğe ekleyemeyeceğini / kaldıramayacağını garanti eder. Varsa, diziyi yeniden ayırır ve orijinali etkilemeyeceğinden emin olur. Bir listenin iadesi, böyle bir güvence yapılmaz ve orijinal etkilenebilir (uygulamaya bağlıdır). Koleksiyondaki öğeleri (dizi veya liste olsun) değiştirmek, öğenin türü bir yapı değilse orijinali etkileyebilir.
Herman Schoenfeld

Açıklama için teşekkürler. Hala kafam karıştı (muhtemelen C ++ dünyasından geldiğim için). Eğer strdahili bir dizi kullanır ve ToCharArraybu dizi için bir başvuru verir istemci mutasyona olabilir strboyutu kalıntıları sabit olsa bile, bu dizinin elemanları değiştirerek. Ancak 'strChars' modifikasyonunun orijinal "str" ​​nesnesini değiştirmeyeceği açıktır. Burada ne eksik? Gördüğüm kadarıyla, her iki durumda da müşteri iç temsile erişebilir ve türüne bakılmaksızın, bu bir tür mutasyona izin verir.
Tim MB

3

Ben ihtiyaca gidiyorum tam olarak kaç unsurlar olduğunu bilirsem 5 elemanları ve sadece ihtiyaç söylemek hiç o zaman bir dizi kullanmak 5 elemanları. Aksi takdirde bir Liste <T> kullanıyorum.


1
Eleman sayısını bildiğinizde neden <T> Listesi kullanmıyorsunuz?
Oliver

3

Çoğu zaman, bir kullanmak Listyeterlidir. A List, verilerini işlemek için dahili bir dizi kullanır Listve mevcut kapasitesinden daha fazla öğe eklerken diziyi otomatik olarak yeniden boyutlandırır , bu da kapasiteyi önceden bilmeniz gereken bir diziden daha kolay hale getirir.

Bkz. Http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5C # 'daki Listeler hakkında daha fazla bilgi edinmek için veya yalnızca kaynak kodunu kullanın.System.Collections.Generic.List<T> .

Çok boyutlu verilere ihtiyacınız varsa (örneğin bir matris veya grafik programlamada), muhtemelen bir array bunun yerine .

Her zaman olduğu gibi, bellek veya performans bir sorunsa ölçün! Aksi takdirde kod hakkında yanlış varsayımlar yapabilirsiniz.


1
Merhaba, "Bir listenin arama süresi O (n) olur" neden doğru olduğunu açıklayabilir misiniz? Bildiğim kadarıyla List <T> sahnelerin arkasında dizi kullanıyor.
yusufçuk

1
@dragonfly tamamen haklısın. Kaynak . O zaman, uygulamanın işaretçiler kullandığını varsaydım, ama o zamandan beri başka türlü öğrendim. Yukarıdaki bağlantıdan: 'Bu özelliğin değerini almak bir O (1) işlemidir; özelliğin ayarlanması aynı zamanda bir O (1) işlemidir. '
Sune Rievers

2

Diziler Vs. Listeler klasik bir sürdürülebilirlik ve performans sorunudur. Neredeyse tüm geliştiricilerin takip ettiği temel kural, her ikisi için de ateş etmeniz gerektiğidir, ancak çatışmalara girdiklerinde performans üzerinde sürdürülebilirliği seçin. Bu kuralın istisnası, performansın zaten bir sorun olduğunu kanıtlamış olmasıdır. Bu prensibi Arrays Vs. Listeler, sonra ne elde edersiniz:

Performans sorunlarına ulaşıncaya kadar güçlü yazılan listeler kullanın. Bir performans sorunuyla karşılaşırsanız, dizilere bırakmanın çözüm ile performans açısından çözümünüze bakım açısından çözümünüze zarar vereceğinden daha fazla fayda sağlayıp sağlayamayacağına karar verin.


1

Henüz bahsedilmeyen başka bir durum, her birinin birbirine yapışmış sabit bir grup ilişkili ancak bağımsız değişkenlerden oluşan çok sayıda öğeye sahip olacağıdır (örneğin bir noktanın koordinatları veya bir 3d üçgenin köşeleri). Bir dizi açık alan yapısı, elemanlarının verimli bir şekilde "yerinde" değiştirilmesine izin verecektir - diğer toplama türleriyle mümkün olmayan bir şey. Bir yapı dizisi elemanlarını ardışık olarak RAM'de tuttuğundan, dizi elemanlarına sıralı erişim çok hızlı olabilir. Kodun bir dizi boyunca birçok ardışık geçiş yapması gerektiği durumlarda, bir yapılar dizisi bir dizi veya sınıf nesnesi referanslarının diğer koleksiyonundan 2: 1 faktörüyle daha iyi performans gösterebilir; Daha ileri,

Diziler yeniden boyutlandırılamasa da, kullanımda olan öğelerin sayısıyla birlikte kodun bir dizi başvurusunu depolaması ve diziyi gerektiği gibi daha büyük bir diziyle değiştirmesi zor değildir. Alternatif olarak, çok benzer davranan, List<T>ancak destek deposunu açığa çıkaran bir tür için kod kolayca yazılabilir , böylece ya MyPoints.Add(nextPoint);ya söylenebilir MyPoints.Items[23].X += 5;. Kod listenin sonunun ötesine erişmeye çalıştığında, ikincisinin mutlaka bir istisna atmayacağını, ancak kullanımın kavramsal olarak oldukça benzer olacağını unutmayın List<T>.


Açıkladığınız bir Liste <>. Temel diziye doğrudan erişebilmeniz için bir dizinleyici vardır ve Liste <> sizin için boyutu korur.
Carl

@Carl: Örneğin Point[] arr;, kodun söylenmesi mümkündür, örn arr[3].x+=q;. Örneğin List<Point> list, bunun yerine söylemek gerekir Point temp=list[3]; temp.x+=q; list[3]=temp;. List<T>Bir yöntem olsaydı faydalı olurdu Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params). ve derleyiciler list[3].x+=q;dönüşebilir {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);ancak böyle bir özellik yoktur.
supercat

İyi haberler. İşe yarıyor. list[0].X += 3;listenin ilk öğesinin X özelliğine 3 ekler. Ve lista List<Point>ve PointX ve Y özelliklerine sahip bir sınıftır
Carl

1

.NET'teki listeler diziler üzerindeki sarmalayıcılardır ve dahili olarak bir dizi kullanırlar. Listelerdeki işlemlerin zaman karmaşıklığı, dizilerle aynıdır, ancak listelerin tüm ek işlevselliği / kullanım kolaylığıyla (otomatik yeniden boyutlandırma ve liste sınıfıyla birlikte gelen yöntemler gibi) biraz daha fazla ek yük vardır. Oldukça, son derece optimize edilmiş kod yazmanız gerekiyorsa veya diziler etrafında inşa edilmiş diğer kodlarla çalışıyorsanız, bunu yapmamak için zorlayıcı bir neden olmadığı sürece her durumda listelerin kullanılmasını öneriyorum .


0

Her veri türünün özelliklerinin karşılaştırılmasından ziyade, en pragmatik cevap "özellikle ikisi de uyguladığından beri yapmanız gereken şey için farklılıklar muhtemelen o kadar önemli değil IEnumerable, bu yüzden popüler sözleşmeyi takip edin ve bir Listbir sebep olmadan, muhtemelen bu noktada bir diziyi a List.

Çoğu zaman yönetilen kodda koleksiyonların mikro optimizasyonlar hakkında endişelenmeyle mümkün olduğunca kolay çalışmasını tercih edersiniz.


0

Onlar popüler olmayabilir, ama ben oyun projelerinde Diziler hayranıyım. - Yineleme hızı bazı durumlarda önemli olabilir, bir Array üzerindeki her öğenin öğe başına fazla bir şey yapmıyorsanız önemli ölçüde daha az yükü vardır - Ekleme ve kaldırma yardımcı işlevlerle o kadar zor değildir - Daha yavaş, ancak yalnızca bir kez oluşturduğunuz durumlarda önemli olmayabilir - Çoğu durumda, daha az fazla bellek boşa harcanır (yalnızca yapı dizileri ile gerçekten önemlidir) - Biraz daha az çöp ve işaretçi ve işaretçi kovalaması

Bununla birlikte, Listeyi pratikte Dizilerden çok daha sık kullanıyorum, ama her birinin yeri var.

Liste, sarıcı ve numaralandırma yükünü optimize edebilmeleri için yerleşik bir türün olması iyi olurdu.


0

Liste doldurmak bir diziden daha kolaydır. Diziler için, verilerin tam uzunluğunu bilmeniz gerekir, ancak listeler için veri boyutu herhangi biri olabilir. Ve bir listeyi diziye dönüştürebilirsiniz.

List<URLDTO> urls = new List<URLDTO>();

urls.Add(new URLDTO() {
    key = "wiki",
    url = "https://...",
});

urls.Add(new URLDTO()
{
    key = "url",
    url = "http://...",
});

urls.Add(new URLDTO()
{
    key = "dir",
    url = "https://...",
});

// convert a list into an array: URLDTO[]
return urls.ToArray();

0

Kimse bahsetmediği için: C # 'da bir dizi bir listedir. MyClass[]ve List<MyClass>her ikisi de uygular IList<MyClass>. (örneğin void Foo(IList<int> foo)şöyle Foo(new[] { 1, 2, 3 })veyaFoo(new List<int> { 1, 2, 3 }) )

Bu nedenle, a'yı List<MyClass>bağımsız değişken olarak kabul eden ancak yalnızca özellik alt kümesini kullanan bir yöntem yazıyorsanız , şu şekilde bildirmek isteyebilirsiniz:IList<MyClass> bunun yerine arayanların rahatlığı için .

Detaylar:


"C # 'da bir dizi bir listedir" Bu doğru değil; bir dizi bir değil List, sadece IListarayüzü uygular .
Rufus L

-1

Tamamen veri yapısının gerekli olduğu bağlamlara bağlıdır. Örneğin, Listeyi kullanarak diğer işlevler veya hizmetler tarafından kullanılacak öğeleri oluşturuyorsanız, bunu başarmanın mükemmel yoludur.

Şimdi bir öğe listeniz varsa ve bunları görüntülemek istiyorsanız, bir web sayfası dizisinde kullanmanız gereken kapsayıcı olduğunu söyleyin.


1
Bir öğe listeniz varsa ve bunları görüntülemek istiyorsanız, zaten sahip olduğunuz listeyi kullanmanın nesi yanlış? Bir dizi burada ne sunar?
Marc Gravell

1
Ve "diğer işlevler veya hizmetler tarafından kullanılacak öğeler oluşturmak" için, aslında, bir yineleyici blok ile tercih ediyorum IEnumerable<T>- o zaman onları tamponlamak yerine nesneleri akışı.
Marc Gravell

-1

Yerinde döküm yapma yeteneğinden bahsetmeye değer.

interface IWork { }
class Foo : IWork { }

void Test( )
{
    List<Foo> bb = new List<Foo>( );
    // Error: CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<Foo>' to 'System.Collections.Generic.List<IWork>'
    List<IWork> cc = bb; 

    Foo[] bbb = new Foo[4];
    // Fine
    IWork[] ccc = bbb;
}

Dolayısıyla Array , dönüş türü veya işlevler için bağımsız değişkente kullanıldığında biraz daha esneklik sunar.

IWork[] GetAllWorks( )
{
    List<Foo> fooWorks = new List<Foo>( );
    return fooWorks.ToArray( ); // Fine
}

void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]

Sen dizinin varyans belirtmeliyiz edilir kırık .
mcarton
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.