Saya tahu bahwa array instantiated dari tipe nilai dalam C # secara otomatis diisi dengan nilai default tipe (misalnya false untuk bool, 0 untuk int, dll).
Apakah ada cara untuk mengisi array secara otomatis dengan nilai seed yang bukan default? Baik pada pembuatan atau metode bawaan sesudahnya (seperti Java's Arrays.fill () )? Katakanlah saya ingin array boolean yang benar secara default, bukan false. Apakah ada cara built-in untuk melakukan ini, atau Anda hanya perlu beralih melalui array dengan for for?
// Example pseudo-code:
bool[] abValues = new[1000000];
Array.Populate(abValues, true);
// Currently how I'm handling this:
bool[] abValues = new[1000000];
for (int i = 0; i < 1000000; i++)
{
abValues[i] = true;
}
Harus beralih melalui array dan "mengatur ulang" setiap nilai ke true tampaknya tidak efisien. Apakah ada masalah di sekitar ini? Mungkin dengan membalik semua nilai?
Setelah mengetik pertanyaan ini dan memikirkannya, saya menduga bahwa nilai default hanyalah hasil dari bagaimana C # menangani alokasi memori objek-objek ini di belakang layar, jadi saya membayangkan mungkin tidak mungkin untuk melakukan ini. Tapi saya masih ingin tahu pasti!
sumber
Jawaban:
Tidak tahu metode kerangka kerja tetapi Anda bisa menulis bantuan cepat untuk melakukannya untuk Anda.
sumber
int[] arr = new int[16].Populate(-1);
void
keT[]
dan kemudian Anda dapat melakukanvar a = new int[100].Polupate(1)
sumber
Enumerable.ToArray
tidak tahu ukuran urutan enumerable, sehingga harus menebak ukuran array. Itu berarti Anda akan mendapatkan alokasi array setiap kaliToArray
buffer terlampaui, ditambah satu alokasi lagi di akhir trim. Ada juga overhead yang terlibat dengan objek enumerable.Buat array baru dengan seribu
true
nilai:Demikian pula, Anda dapat menghasilkan urutan integer:
sumber
Untuk array atau array besar yang akan berukuran variabel, Anda mungkin harus menggunakan:
Untuk array kecil Anda dapat menggunakan sintaks inisialisasi koleksi di C # 3:
Manfaat sintaks inisialisasi koleksi, adalah Anda tidak harus menggunakan nilai yang sama di setiap slot dan Anda dapat menggunakan ekspresi atau fungsi untuk menginisialisasi slot. Juga, saya pikir Anda menghindari biaya inisialisasi slot array ke nilai default. Jadi, misalnya:
sumber
float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Jika array Anda terlalu besar, Anda harus menggunakan BitArray. Ini menggunakan 1 bit untuk setiap bool, bukan byte (seperti dalam array bools) juga Anda dapat mengatur semua bit menjadi true dengan operator bit. Atau hanya menginisialisasi pada true. Jika Anda hanya perlu melakukannya sekali, itu hanya akan memakan biaya lebih banyak.
sumber
Anda dapat menggunakan
Array.Fill
.NET Core 2.0+ dan .NET Standard 2.1+.sumber
sayangnya saya tidak berpikir ada cara langsung, namun saya pikir Anda dapat menulis metode ekstensi untuk kelas array untuk melakukan ini
sumber
Nah setelah sedikit lebih banyak googling dan membaca saya menemukan ini:
Yang pasti lebih dekat dengan apa yang saya cari. Tapi saya tidak yakin apakah itu lebih baik daripada iterasi melalui array asli dalam for-loop dan hanya mengubah nilainya. Setelah tes cepat pada kenyataannya, tampaknya lebih lambat sekitar faktor 5. Jadi bukan solusi yang baik kalau begitu!
sumber
Bagaimana dengan implementasi paralel
Ketika hanya menginisialisasi array, kekuatan kode ini tidak dapat dilihat tetapi saya pikir Anda harus melupakan "pure" untuk.
sumber
Kode di bawah ini menggabungkan iterasi sederhana untuk salinan kecil dan Array.Copy untuk salinan besar
Tolok ukur untuk panjang array yang berbeda menggunakan array int [] adalah:
Kolom pertama adalah ukuran array, diikuti oleh waktu penyalinan menggunakan iterasi sederhana (implementasi @JaredPared). Waktu metode ini adalah setelah itu. Ini adalah tolok ukur menggunakan array struct empat bilangan bulat
sumber
Atau ... Anda bisa menggunakan logika terbalik. Biarkan
false
berartitrue
dan sebaliknya.Contoh kode
sumber
bool[] isVisible
membuatnyabool[] isHidden
ini juga berfungsi ... tetapi mungkin tidak perlu
sumber
Banyak jawaban yang disajikan di sini bermuara pada satu loop yang menginisialisasi elemen array satu per satu, yang tidak memanfaatkan instruksi CPU yang dirancang untuk beroperasi pada blok memori sekaligus.
.Net Standar 2.1 (dalam pratinjau pada saat penulisan ini) menyediakan Array.Fill () , yang cocok untuk implementasi berkinerja tinggi di pustaka runtime (meskipun seperti yang sekarang, .NET Core tampaknya tidak memanfaatkan kemungkinan itu) .
Untuk mereka yang berada di platform sebelumnya, metode ekstensi berikut mengungguli loop sepele dengan margin substansial ketika ukuran array signifikan. Saya membuatnya ketika solusi saya untuk tantangan kode online sekitar 20% dari anggaran waktu yang dialokasikan. Ini mengurangi runtime sekitar 70%. Dalam hal ini, pengisian array dilakukan di dalam loop lain. BLOCK_SIZE ditetapkan oleh firasat daripada eksperimen. Beberapa optimisasi dimungkinkan (misalnya menyalin semua byte yang telah ditetapkan ke nilai yang diinginkan daripada blok ukuran tetap).
sumber
Jika Anda berencana untuk hanya menetapkan beberapa nilai dalam array, tetapi sebagian besar ingin mendapatkan nilai default (khusus), Anda dapat mencoba sesuatu seperti ini:
Anda mungkin perlu mengimplementasikan antarmuka lain untuk membuatnya berguna, seperti yang ada di array itu sendiri.
sumber
Tidak ada cara untuk mengatur semua elemen dalam array sebagai operasi tunggal, KECUALI, nilai itu adalah nilai default jenis elemen.
Misalnya, jika ini adalah array bilangan bulat, Anda dapat mengatur semuanya ke nol dengan satu operasi, seperti:
Array.Clear(...)
sumber
Saya sadar saya terlambat ke pesta, tapi ini ide. Tulis pembungkus yang memiliki operator konversi ke dan dari nilai terbungkus sehingga dapat digunakan sebagai pengganti untuk jenis yang dibungkus. Ini sebenarnya terinspirasi oleh jawaban yang terdengar konyol dari @ l33t.
Pertama (berasal dari C ++) saya menyadari bahwa di C # default ctor tidak dipanggil ketika elemen array dibangun. Alih-alih - bahkan di hadapan konstruktor default yang ditentukan pengguna! - semua elemen array diinisialisasi nol. Itu mengejutkan saya.
Jadi kelas wrapper yang hanya menyediakan ctor default dengan nilai yang diinginkan akan bekerja untuk array dalam C ++ tetapi tidak dalam C #. Solusinya adalah membiarkan pembungkus tipe memetakan 0 ke nilai benih yang diinginkan pada saat konversi. Dengan begitu nol nilai yang diinisialisasi tampaknya diinisialisasi dengan seed untuk semua tujuan praktis:
Pola ini berlaku untuk semua tipe nilai. Seseorang dapat misalnya memetakan 0 hingga 4 untuk int jika diinginkan inisialisasi dengan 4 dll.
Saya ingin membuat templat seperti yang dimungkinkan dalam C ++, memberikan nilai seed sebagai parameter templat, tapi saya mengerti itu tidak mungkin dalam C #. Atau apakah saya melewatkan sesuatu? (Tentu saja dalam pemetaan C ++ tidak diperlukan sama sekali karena seseorang dapat memberikan ctor default yang akan dipanggil untuk elemen array.)
FWIW, ini setara dengan C ++: https://ideone.com/wG8yEh .
sumber
Jika Anda dapat membalikkan logika Anda, Anda dapat menggunakan
Array.Clear()
metode untuk mengatur array boolean ke false.sumber
Jika Anda menggunakan .NET Core, .NET Standard> = 2.1, atau bergantung pada paket System.Memory, Anda juga dapat menggunakan
Span<T>.Fill()
metode ini:https://dotnetfiddle.net/UsJ9bu
sumber
Berikut versi lain untuk kami pengguna Kerangka yang ditinggalkan oleh Microsoft. Ini 4 kali lebih cepat
Array.Clear
dan lebih cepat daripada solusi Panos Theof dan paralel Eric J dan Petar Petrov - hingga dua kali lebih cepat untuk array besar.Pertama, saya ingin menyajikan kepada Anda leluhur fungsi tersebut, karena itu membuatnya lebih mudah untuk memahami kodenya. Kinerja-bijaksana ini cukup setara dengan kode Panos Theof, dan untuk beberapa hal yang mungkin sudah cukup:
Seperti yang Anda lihat, ini didasarkan pada penggandaan berulang dari bagian yang sudah diinisialisasi. Ini sederhana dan efisien, tetapi bertabrakan dengan arsitektur memori modern. Oleh karena itu lahir versi yang menggunakan penggandaan hanya untuk membuat blok seed yang ramah cache, yang kemudian diledakkan berulang-ulang di atas area target:
Catatan: kode sebelumnya diperlukan
(count + 1) >> 1
sebagai batas untuk loop pengganda untuk memastikan bahwa operasi penyalinan akhir memiliki cukup makanan untuk mencakup semua yang tersisa. Ini tidak akan menjadi kasus perhitungan ganjil jikacount >> 1
digunakan sebagai gantinya. Untuk versi saat ini, ini tidak penting karena loop salinan linier akan mengambil setiap kelonggaran.Ukuran sel array harus dilewatkan sebagai parameter karena - boggles pikiran - generik tidak diizinkan untuk digunakan
sizeof
kecuali mereka menggunakan constraint (unmanaged
) yang mungkin atau mungkin tidak tersedia di masa depan. Perkiraan yang salah bukanlah masalah besar tetapi kinerja terbaik jika nilainya akurat, karena alasan berikut:Meremehkan ukuran elemen dapat menyebabkan ukuran blok lebih besar dari setengah cache L1, sehingga meningkatkan kemungkinan data sumber salinan diusir dari L1 dan harus diambil kembali dari tingkat cache yang lebih lambat.
Menaksir terlalu tinggi ukuran elemen menghasilkan utilisasi yang kurang dari cache L1 CPU, yang berarti loop copy blok linier dieksekusi lebih sering daripada pemanfaatan optimal. Dengan demikian, lebih dari overhead loop / panggilan tetap yang dikeluarkan daripada yang diperlukan.
Berikut adalah patokan mengadu kode saya
Array.Clear
dan tiga solusi lainnya yang disebutkan sebelumnya. Pengaturan waktu adalah untuk mengisi bilangan bulat array (Int32[]
) dari ukuran yang diberikan. Untuk mengurangi variasi yang disebabkan oleh cache vagaries dll. Setiap tes dieksekusi dua kali, kembali ke belakang, dan waktunya diambil untuk eksekusi kedua.Jika kinerja kode ini tidak mencukupi maka jalan yang menjanjikan akan memparalelkan loop copy linier (dengan semua utas menggunakan blok sumber yang sama), atau teman lama kita P / Invoke.
Catatan: membersihkan dan mengisi blok biasanya dilakukan dengan rutinitas runtime yang bercabang ke kode yang sangat khusus menggunakan instruksi MMX / SSE dan yang lainnya, jadi dalam lingkungan yang layak orang hanya akan memanggil setara moral masing-masing
std::memset
dan dijamin tingkat kinerja profesional. TKI, berdasarkan haknya fungsi perpustakaanArray.Clear
harus meninggalkan semua versi lintingan tangan kami dalam debu. Fakta bahwa itu sebaliknya menunjukkan betapa jauh dari hal-hal yang sebenarnya. Hal yang sama berlaku karena harus menggulung sendiriFill<>
di tempat pertama, karena masih hanya dalam Core dan Standard tetapi tidak dalam Framework. .NET telah ada selama hampir dua puluh tahun sekarang dan kita masih harus P / Panggil kiri dan kanan untuk hal-hal yang paling mendasar atau menggulung ...sumber
Ada beberapa jawaban lagi untuk pertanyaan (rangkap?) Ini: Apa yang setara dengan memset di C #?
Seseorang telah membuat tolok ukur alternatif (mereka memasukkan versi yang tidak aman, tetapi mereka tidak mencoba
memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.htmlsumber
Berikut ini adalah penilaian lain
System.Collections.BitArray
yang memiliki konstruktor tersebut.atau
sumber
Buat kelas privat di dalam di mana Anda membuat array dan memiliki pengambil dan penyetel untuk itu. Kecuali jika Anda memerlukan setiap posisi dalam array untuk menjadi sesuatu yang unik, seperti acak, lalu gunakan int? sebagai array dan kemudian dapatkan jika posisinya nol, isi posisi itu dan kembalikan nilai acak yang baru.
Atau gunakan
Sebagai setter.
sumber
sumber