Array versus Daftar <T>: Kapan menggunakan yang mana?

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

Apa skenario ketika satu lebih disukai daripada yang lain? Dan mengapa?

Frederick The Fool
sumber
9
Array agak usang, seperti yang terlihat dalam diskusi populer di sini. Juga ditunjukkan di sini , dan oleh tuan rumah kami di blog .
gimel
9
Jika saya tidak salah, Daftar <> memiliki susunan sebagai struktur internal. Setiap kali array internal diisi, cukup salin konten ke array yang dua kali lipat ukuran (atau beberapa kali konstan lainnya dari ukuran saat ini). en.wikipedia.org/wiki/Dynamic_array
Ykok
Ykok: Apa yang Anda katakan sepertinya benar, saya menemukan kode sumber Daftar <> di sini .
Carol
19
@gimel Berdebat bahwa array sudah usang mungkin agak berani
awdz9nld

Jawaban:

580

Jarang, pada kenyataannya, Anda ingin menggunakan array. Pasti menggunakan waktu List<T>kapan saja Anda ingin menambah / menghapus data, karena mengubah ukuran array mahal. Jika Anda tahu datanya panjang tetap, dan Anda ingin mengoptimalkan mikro untuk beberapa alasan yang sangat spesifik (setelah benchmarking), maka sebuah array mungkin berguna.

List<T>menawarkan lebih banyak fungsionalitas daripada sebuah array (walaupun LINQ sedikit meratakannya), dan hampir selalu merupakan pilihan yang tepat. Kecuali untuk paramsargumen, tentu saja. ;-p

Sebagai penghitung - List<T>adalah satu dimensi; di mana-seperti Anda memiliki array persegi panjang (dll) seperti int[,]atau string[,,]- tetapi ada cara lain untuk memodelkan data tersebut (jika Anda perlu) dalam model objek.

Lihat juga:

Yang mengatakan, saya menggunakan banyak array dalam proyek protobuf-net saya ; sepenuhnya untuk kinerja:

  • ia melakukan banyak bit-shifting, jadi a byte[]sangat penting untuk encoding;
  • Saya menggunakan rolling byte[]buffer lokal yang saya isi sebelum mengirim ke aliran yang mendasarinya (dan vv); lebih cepat dari BufferedStreamdll;
  • itu secara internal menggunakan model objek berbasis array ( Foo[]bukan List<Foo>), karena ukurannya diperbaiki setelah dibangun, dan harus sangat cepat.

Tapi ini jelas pengecualian; untuk pemrosesan lini bisnis umum, List<T>setiap kali menang.

Marc Gravell
sumber
8
Argumen tentang mengubah ukuran benar-benar valid. Namun orang-orang lebih suka Daftar bahkan ketika tidak perlu mengubah ukuran. Untuk kasus yang terakhir ini, apakah ada argumen yang solid, logis atau apakah itu tidak lebih dari "susunan mode"?
Frederick The Fool
6
"Pasti menggunakan Daftar <T> kapan saja Anda ingin menambah / menghapus data, karena mengubah ukuran array mahal." Daftar <T> menggunakan array secara internal. Apakah Anda memikirkan LinkedList <T>?
dan-gph
14
Lebih banyak fitur == lebih kompleks == tidak baik, kecuali Anda membutuhkan fitur-fitur itu. Jawaban ini pada dasarnya mencantumkan alasan mengapa array lebih baik, namun menarik kesimpulan yang berlawanan.
Eamon Nerbonne
12
@EamonNerbonne jika Anda tidak menggunakan fitur-fitur itu, saya bisa menjamin bahwa mereka tidak akan menyakiti Anda ... tetapi: jumlah koleksi yang tidak pernah membutuhkan mutasi jauh lebih kecil, menurut pengalaman saya, daripada yang ada bermutasi
Marc Gravell
7
@MarcGravell: itu tergantung pada gaya pengkodean Anda. Dalam pengalaman saya hampir tidak ada koleksi yang pernah bermutasi. Itu adalah; koleksi diambil dari database atau dibangun dari beberapa sumber, tetapi pemrosesan lebih lanjut selalu dilakukan dengan membuat ulang koleksi baru (misalnya peta / filter dll). Bahkan ketika mutasi konseptual diperlukan, ia cenderung paling sederhana untuk hanya menghasilkan koleksi baru. Saya hanya pernah memutasikan koleksi sebagai pengoptimalan kinerja, dan pengoptimalan semacam itu cenderung sangat lokal dan tidak memaparkan mutasi ke konsumen API.
Eamon Nerbonne
121

Benar-benar hanya menjawab untuk menambahkan tautan yang saya terkejut belum disebutkan: entri blog Eric Lippert di "Array dianggap agak berbahaya."

Anda dapat menilai dari judul yang disarankan menggunakan koleksi di mana pun praktis - tetapi seperti yang ditunjukkan Marc dengan tepat, ada banyak tempat di mana array benar-benar merupakan satu-satunya solusi praktis.

Jon Skeet
sumber
2
Akhirnya sempat membaca ini lebih dari 3 tahun kemudian haha. Artikel bagus, artikel bagus sekarang. :)
Spencer Ruport
21

Terlepas dari jawaban lain yang merekomendasikan List<T>, Anda harus menggunakan array saat menangani:

  • data bitmap gambar
  • struktur data tingkat rendah lainnya (yaitu protokol jaringan)
Alnitak
sumber
1
Mengapa protokol jaringan? Bukankah Anda lebih suka menggunakan struktur khusus di sini dan memberi mereka serializer khusus atau tata letak memori eksplisit? Selanjutnya, apa yang menentang penggunaan List<T>array di sini daripada byte?
Konrad Rudolph
8
@ Konrad - baik, untuk pemula, Stream.Read dan Stream.Write bekerja dengan byte [], seperti halnya Encoding dll ...
Marc Gravell
12

Kecuali jika Anda benar-benar peduli dengan kinerja, dan maksud saya, "Mengapa Anda menggunakan .Net, bukan C ++?" Anda harus tetap menggunakan List <>. Lebih mudah untuk mempertahankan dan melakukan semua pekerjaan kotor mengubah ukuran array di belakang layar untuk Anda. (Jika perlu, Daftar <> cukup pintar dalam memilih ukuran array sehingga tidak perlu biasanya.)

Spencer Ruport
sumber
15
"Kenapa kamu menggunakan .Net, bukan C ++?" XNA
Bengt
6

Array harus digunakan dalam preferensi ke Daftar ketika imutabilitas koleksi itu sendiri adalah bagian dari kontrak antara kode klien & penyedia (tidak harus immutabilitas item dalam koleksi) DAN ketika IEnumerable tidak cocok.

Sebagai contoh,

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

Jelas bahwa modifikasi "strChars" tidak akan bermutasi objek "str" ​​asli, terlepas dari pengetahuan tingkat implementasi dari tipe "str" ​​yang mendasarinya.

Tapi anggap itu

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

Dalam kasus ini, tidak jelas hanya dari snipet kode itu jika metode penyisipan akan atau tidak akan bermutasi objek "str" ​​asli. Dibutuhkan pengetahuan tingkat implementasi dari String untuk membuat tekad itu, yang mematahkan pendekatan Design by Contract. Dalam kasus String, ini bukan masalah besar, tetapi bisa menjadi masalah besar di hampir setiap kasus lainnya. Mengatur Daftar hanya-baca tidak membantu tetapi menghasilkan kesalahan run-time, bukan waktu kompilasi.

Herman Schoenfeld
sumber
Saya relatif baru di C # tetapi tidak jelas bagi saya mengapa mengembalikan daftar akan menyarankan ketidakmampuan data asli dengan cara mengembalikan array tidak. Saya akan berpikir bahwa metode yang namanya dimulai dengan Toakan membuat objek yang tidak memiliki kemampuan untuk mengubah contoh asli, yang bertentangan dengan strChars as char[]yang jika valid akan menyarankan Anda sekarang dapat memodifikasi objek asli.
Tim MB
@TimMB Ada kekekalan koleksi (tidak dapat menambah atau menjauhkan item) dan kekekalan dari item dalam koleksi. Saya mengacu pada yang terakhir, sedangkan Anda mungkin menggabungkan keduanya. Mengembalikan array meyakinkan klien bahwa ia tidak dapat menambah / menghapus item. Jika ya, itu akan mengalokasikan kembali array dan yakin itu tidak akan memengaruhi yang asli. Mengembalikan daftar, tidak ada jaminan seperti itu dibuat dan asli dapat dipengaruhi (tergantung pada implementasi). Mengubah item dalam koleksi (apakah array atau daftar) dapat memengaruhi yang asli, jika jenis item bukan struct.
Herman Schoenfeld
Terimakasih atas klarifikasinya. Saya masih bingung (mungkin karena saya berasal dari dunia C ++). Jika secara strinternal menggunakan array dan ToCharArraymengembalikan referensi ke array ini maka klien dapat bermutasi strdengan mengubah elemen-elemen array itu, bahkan jika ukurannya tetap. Namun Anda menulis 'Jelas bahwa modifikasi "strChars" tidak akan mengubah objek "str" ​​yang asli. Apa yang kulewatkan di sini? Dari apa yang saya lihat, dalam kedua kasus klien dapat memiliki akses ke representasi internal dan, terlepas dari jenisnya, ini akan memungkinkan mutasi dari beberapa jenis.
Tim MB
3

Jika saya tahu persis berapa banyak elemen aku akan kebutuhan, mengatakan saya perlu 5 elemen dan hanya pernah 5 elemen maka saya menggunakan array. Kalau tidak, saya hanya menggunakan Daftar <T>.

smack0007
sumber
1
Mengapa Anda tidak menggunakan Daftar <T> jika Anda tahu jumlah elemen?
Oliver
3

Sebagian besar waktu, menggunakan Listakan cukup. A Listmenggunakan larik internal untuk menangani datanya, dan secara otomatis mengubah ukuran larik saat menambahkan lebih banyak elemen Listdaripada kapasitas saat ini, yang membuatnya lebih mudah digunakan daripada larik, di mana Anda perlu mengetahui kapasitasnya sebelumnya.

Lihat http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5 untuk informasi lebih lanjut tentang Daftar di C # atau hanya mendekompilasi System.Collections.Generic.List<T>.

Jika Anda membutuhkan data multidimensi (misalnya menggunakan matriks atau dalam pemrograman grafis), Anda mungkin akan memilih yang arraysebaliknya.

Seperti biasa, jika memori atau kinerja bermasalah, ukurlah! Kalau tidak, Anda bisa membuat asumsi yang salah tentang kode.

Sune Rievers
sumber
1
Hai, dapatkah Anda menjelaskan mengapa "Waktu pencarian daftar adalah O (n)" benar? Sejauh yang saya tahu Daftar <T> menggunakan array di belakang layar.
capung
1
@dragonfly kau benar sekali. Sumber . Pada saat itu, saya berasumsi bahwa implementasi menggunakan pointer, tetapi saya sudah belajar sebaliknya. Dari tautan di atas: 'Mengambil nilai properti ini adalah operasi O (1); mengatur properti juga merupakan operasi O (1). '
Sune Rievers
2

Array Vs. Daftar adalah masalah rawatan klasik vs. kinerja. Aturan praktis yang diikuti oleh hampir semua pengembang adalah Anda harus menembak untuk keduanya, tetapi ketika mereka mengalami konflik, pilihlah rawatan daripada kinerja. Pengecualian untuk aturan itu adalah ketika kinerja telah terbukti menjadi masalah. Jika Anda membawa prinsip ini ke Array Vs. Daftar, lalu yang Anda dapatkan adalah ini:

Gunakan daftar yang sangat diketik sampai Anda menekan masalah kinerja. Jika Anda mengalami masalah kinerja, buat keputusan apakah drop out ke array akan menguntungkan solusi Anda dengan kinerja lebih daripada itu akan merugikan solusi Anda dalam hal pemeliharaan.

Pengembang Melbourne
sumber
1

Situasi lain yang belum disebutkan adalah ketika seseorang akan memiliki sejumlah besar item, yang masing-masing terdiri dari sekelompok variabel terkait-tetapi-independen yang saling menempel (mis. Koordinat suatu titik, atau simpul dari segitiga 3d). Susunan struktur bidang terbuka akan memungkinkan elemen-elemennya dimodifikasi secara efisien "di tempat" - sesuatu yang tidak mungkin dilakukan dengan jenis koleksi lainnya. Karena array struktur memegang elemen-elemennya secara berurutan dalam RAM, akses berurutan ke elemen array bisa sangat cepat. Dalam situasi di mana kode perlu membuat banyak melewati sekuensial melalui array, array struktur dapat mengungguli array atau kumpulan referensi objek kelas lainnya dengan faktor 2: 1; lebih lanjut,

Meskipun array tidak dapat diubah ukurannya, tidak sulit untuk memiliki kode yang menyimpan referensi array bersama dengan jumlah elemen yang digunakan, dan mengganti array dengan yang lebih besar sesuai kebutuhan. Atau, seseorang dapat dengan mudah menulis kode untuk tipe yang berperilaku seperti List<T>tetapi membuka backing store-nya, sehingga memungkinkan seseorang untuk mengatakan salah satu MyPoints.Add(nextPoint);atau MyPoints.Items[23].X += 5;. Perhatikan bahwa yang terakhir tidak perlu membuang pengecualian jika kode mencoba mengakses di luar akhir daftar, tetapi penggunaan sebaliknya akan sangat mirip secara konseptual List<T>.

supercat
sumber
Apa yang Anda gambarkan adalah Daftar <>. Ada pengindeks sehingga Anda dapat mengakses array yang mendasarinya secara langsung, dan Daftar <> akan mempertahankan ukurannya untuk Anda.
Carl
@Carl: Diberikan misalnya Point[] arr;, mungkin untuk kode mengatakan, misalnya arr[3].x+=q;. Menggunakan misalnya List<Point> list, akan perlu untuk mengatakan sebaliknya Point temp=list[3]; temp.x+=q; list[3]=temp;. Akan sangat membantu jika List<T>punya metode Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params). dan kompiler bisa berubah list[3].x+=q;menjadi {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);tetapi tidak ada fitur seperti itu.
supercat
Kabar baik. Berhasil. list[0].X += 3;akan menambahkan 3 ke properti X dari elemen pertama dari daftar. Dan listadalah List<Point>dan Pointmerupakan kelas dengan sifat X dan Y
Carl
1

Daftar di .NET adalah pembungkus di atas array, dan menggunakan array secara internal. Kompleksitas waktu operasi pada daftar adalah sama dengan array, namun ada sedikit biaya tambahan dengan semua fungsionalitas tambahan / kemudahan penggunaan daftar (seperti pengubahan ukuran otomatis dan metode yang disertakan dengan kelas daftar). Cukup banyak, saya akan merekomendasikan menggunakan daftar dalam semua kasus kecuali ada alasan kuat untuk tidak melakukannya, seperti jika Anda perlu menulis kode yang sangat optimal, atau bekerja dengan kode lain yang dibangun di sekitar array.

iliketocode
sumber
0

Daripada melakukan perbandingan fitur dari masing-masing tipe data, saya pikir jawaban yang paling pragmatis adalah "perbedaan mungkin tidak begitu penting untuk apa yang perlu Anda capai, terutama karena keduanya diterapkan IEnumerable, jadi ikuti konvensi populer dan gunakan Listsampai Anda memiliki alasan untuk tidak, pada titik mana Anda mungkin akan memiliki alasan untuk menggunakan array di atas List. "

Sebagian besar waktu dalam kode terkelola Anda akan ingin mendukung koleksi menjadi semudah mungkin untuk dikerjakan karena terlalu khawatir tentang optimasi mikro.

moarboilerplate
sumber
0

Mereka mungkin tidak populer, tapi saya penggemar Array dalam proyek game. - Kecepatan Iterasi dapat menjadi penting dalam beberapa kasus, karena pada suatu Array memiliki overhead yang jauh lebih sedikit jika Anda tidak melakukan banyak per elemen - Menambah dan menghapus tidak begitu sulit dengan fungsi pembantu - Lebih lambat, tetapi dalam kasus di mana Anda hanya membangunnya sekali itu mungkin tidak masalah - Dalam kebanyakan kasus, memori ekstra kurang terbuang (hanya benar-benar signifikan dengan Array struct) - Sedikit lebih sedikit sampah dan pointer dan pengejaran pointer

Yang sedang berkata, saya menggunakan Daftar jauh lebih sering daripada Array dalam praktek, tetapi mereka masing-masing memiliki tempat masing-masing.

Akan lebih baik jika Daftar di mana tipe built in sehingga mereka dapat mengoptimalkan pembungkus dan overhead enumerasi.


sumber
0

Mengisi daftar lebih mudah daripada array. Untuk array, Anda perlu mengetahui panjang data yang tepat, tetapi untuk daftar, ukuran data bisa berapa saja. Dan, Anda dapat mengubah daftar menjadi array.

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();
Bimal Poudel
sumber
0

Karena tidak ada yang menyebutkan: Dalam C #, array adalah daftar. MyClass[]dan List<MyClass>keduanya mengimplementasikan IList<MyClass>. (mis. void Foo(IList<int> foo)bisa disebut suka Foo(new[] { 1, 2, 3 })atau Foo(new List<int> { 1, 2, 3 }))

Jadi, jika Anda menulis metode yang menerima List<MyClass>argumen, tetapi hanya menggunakan subset fitur, Anda mungkin ingin mendeklarasikan sebagai IList<MyClass>gantinya untuk kenyamanan penelepon.

Detail:

snipsnipsnip
sumber
"Dalam C #, array adalah daftar" Itu tidak benar; array bukan List, hanya mengimplementasikan IListantarmuka.
Rufus L
-1

Ini sepenuhnya tergantung pada konteks di mana struktur data diperlukan. Misalnya, jika Anda membuat item untuk digunakan oleh fungsi atau layanan lain menggunakan Daftar adalah cara sempurna untuk mencapainya.

Sekarang jika Anda memiliki daftar item dan Anda hanya ingin menampilkannya, katakanlah pada array halaman web adalah wadah yang perlu Anda gunakan.

sajidnizami
sumber
1
Jika Anda memiliki daftar item dan Anda hanya ingin menampilkannya, lalu apa yang salah dengan menggunakan daftar yang sudah Anda miliki? Apa yang akan ditawarkan array di sini?
Marc Gravell
1
Dan untuk "membuat item untuk digunakan oleh fungsi atau layanan lain", sebenarnya, saya lebih suka blok iterator dengan IEnumerable<T>- maka saya dapat melakukan streaming objek daripada buffer mereka.
Marc Gravell
-1

Perlu disebutkan tentang kemampuan melakukan casting di tempat.

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;
}

Jadi Array menawarkan sedikit lebih banyak fleksibilitas ketika digunakan dalam tipe pengembalian atau argumen untuk fungsi.

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

void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]
Wappenull
sumber
Anda harus mencatat bahwa varians array yang rusak .
mcarton