Haruskah saya menggunakan daftar atau array?

22

Saya sedang mengerjakan formulir windows untuk menghitung UPC untuk nomor item.

Saya berhasil membuat satu yang akan menangani satu nomor item / UPC pada suatu waktu, sekarang saya ingin memperluas dan melakukannya untuk beberapa nomor item / UPC.

Saya sudah mulai dan mencoba menggunakan daftar, tetapi saya terus macet. Saya membuat kelas pembantu:

public class Codes
{
    private string incrementedNumber;
    private string checkDigit;
    private string wholeNumber;
    private string wholeCodeNumber;
    private string itemNumber;

    public Codes(string itemNumber, string incrementedNumber, string checkDigit, string wholeNumber, string wholeCodeNumber)
    {
        this.incrementedNumber = incrementedNumber;
        this.checkDigit = checkDigit;
        this.wholeNumber = wholeNumber;
        this.wholeCodeNumber = wholeCodeNumber;
        this.itemNumber = itemNumber;
    }

    public string ItemNumber
    {
        get { return itemNumber; }
        set { itemNumber = value; }
    }

    public string IncrementedNumber
    { 
        get { return incrementedNumber; }
        set { incrementedNumber = value; } 
    }

    public string CheckDigit
    {
        get { return checkDigit; }
        set { checkDigit = value; }
    }

    public string WholeNumber
    {
        get { return wholeNumber; }
        set { wholeNumber = value; }
    }

    public string WholeCodeNumber
    {
        get { return wholeCodeNumber; }
        set { wholeCodeNumber = value; }
    }

}

Kemudian saya mulai dengan kode saya, tetapi masalahnya adalah prosesnya bertahap, artinya saya mendapatkan nomor item dari kotak-kotak melalui kotak centang dan memasukkannya ke dalam daftar. Kemudian saya mendapatkan UPC terakhir dari basis data, menghapus checkdigit, lalu menambah jumlahnya satu per satu dan memasukkannya ke dalam daftar. Lalu saya menghitung checkdigit untuk nomor baru dan memasukkannya ke dalam daftar. Dan di sini saya sudah mendapatkan Pengecualian Kehabisan Memori. Berikut adalah kode yang saya miliki sejauh ini:

List<Codes> ItemNumberList = new List<Codes>();


    private void buttonSearch2_Click(object sender, EventArgs e)
    {
        //Fill the datasets
        this.immasterTableAdapter.FillByWildcard(this.alereDataSet.immaster, (textBox5.Text));
        this.upccodeTableAdapter.FillByWildcard(this.hangtagDataSet.upccode, (textBox5.Text));
        this.uPCTableAdapter.Fill(this.uPCDataSet.UPC);
        string searchFor = textBox5.Text;
        int results = 0;
        DataRow[] returnedRows;
        returnedRows = uPCDataSet.Tables["UPC"].Select("ItemNumber = '" + searchFor + "2'");
        results = returnedRows.Length;
        if (results > 0)
        {
            MessageBox.Show("This item number already exists!");
            textBox5.Clear();
            //clearGrids();
        }
        else
        {
            //textBox4.Text = dataGridView1.Rows[0].Cells[1].Value.ToString();
            MessageBox.Show("Item number is unique.");
        }
    }

    public void checkMarks()
    {

        for (int i = 0; i < dataGridView7.Rows.Count; i++)
        {
            if ((bool)dataGridView7.Rows[i].Cells[3].FormattedValue)
            {
                {
                    ItemNumberList.Add(new Codes(dataGridView7.Rows[i].Cells[0].Value.ToString(), "", "", "", ""));
                }
            }
        }
    }

    public void multiValue1()
    {
        _value = uPCDataSet.UPC.Rows[uPCDataSet.UPC.Rows.Count - 1]["UPCNumber"].ToString();//get last UPC from database
        _UPCNumber = _value.Substring(0, 11);//strip out the check-digit
        _UPCNumberInc = Convert.ToInt64(_UPCNumber);//convert the value to a number

        for (int i = 0; i < ItemNumberList.Count; i++)
        {
            _UPCNumberInc = _UPCNumberInc + 1;
            _UPCNumberIncrement = Convert.ToString(_UPCNumberInc);//assign the incremented value to a new variable
            ItemNumberList.Add(new Codes("", _UPCNumberIncrement, "", "", ""));//**here I get the OutOfMemoreyException**
        }

        for (int i = 0; i < ItemNumberList.Count; i++)
        {
            long chkDigitOdd;
            long chkDigitEven;
            long chkDigitSubtotal;
            chkDigitOdd = Convert.ToInt64(_UPCNumberIncrement.Substring(0, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(2, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(4, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(6, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(8, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(10, 1));
            chkDigitOdd = (3 * chkDigitOdd);
            chkDigitEven = Convert.ToInt64(_UPCNumberIncrement.Substring(1, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(3, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(5, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(7, 1)) + Convert.ToInt64(_UPCNumberIncrement.Substring(9, 1));
            chkDigitSubtotal = (300 - (chkDigitEven + chkDigitOdd));
            _chkDigit = chkDigitSubtotal.ToString();
            _chkDigit = _chkDigit.Substring(_chkDigit.Length - 1, 1);
            ItemNumberList.Add(new Codes("", "",_chkDigit, "", ""));
        }

Apakah ini cara yang tepat untuk melakukannya, menggunakan daftar, atau haruskah saya melihat cara yang berbeda?

campagnolo_1
sumber
Menggunakan daftar tidak masalah. Iterasi daftar sambil menambahkannya adalah cara yang pasti untuk meledakkan kode Anda, dan menunjukkan masalah dalam logika Anda (atau penulisan kode). Juga, ini adalah bug Anda dan tidak mungkin membantu pengunjung di masa mendatang. Voting untuk ditutup.
Telastyn
2
Sebagai catatan, semua bidang pribadi ini (di Codekelas Anda ) berlebihan dan tidak ada apa-apa selain kebisingan, { get; private set; }sudah cukup.
Konrad Morawski
5
Apakah judul pertanyaan untuk ini benar-benar akurat? Ini tidak benar-benar tampak seperti pertanyaan daftar vs array , sebanyak Bagaimana saya bisa meningkatkan pertanyaan implementasi saya . Yang sedang berkata, jika Anda menambahkan atau menghapus elemen, Anda ingin daftar (atau struktur data fleksibel lainnya). Array hanya benar-benar bagus ketika Anda tahu persis berapa banyak elemen yang Anda butuhkan di awal.
KChaloux
@Khalhal, cukup banyak yang ingin saya ketahui. Apakah daftar cara yang tepat untuk membahas hal ini, atau haruskah saya melihat cara yang berbeda untuk mengejar ini? Jadi sepertinya daftar adalah cara yang baik, saya hanya perlu menyesuaikan logika saya.
campagnolo_1
1
@ Telastyn, saya tidak banyak meminta untuk meningkatkan kode saya untuk menunjukkan apa yang saya coba lakukan.
campagnolo_1

Jawaban:

73

Saya akan memperluas komentar saya:

... jika Anda menambahkan atau menghapus elemen, Anda ingin daftar (atau struktur data fleksibel lainnya). Array hanya benar-benar bagus ketika Anda tahu persis berapa banyak elemen yang Anda butuhkan di awal.

Kerusakan Cepat

Array baik ketika Anda memiliki sejumlah elemen tetap yang tidak mungkin berubah, dan Anda ingin mengaksesnya dengan cara yang tidak berurutan.

  • Ukuran tetap
  • Akses Cepat - O (1)
  • Ubah Ukuran Lambat - O (n) - perlu menyalin setiap elemen ke array baru!

Linked-Lists dioptimalkan untuk penambahan dan pemindahan cepat di kedua ujung, tetapi lambat diakses di tengah.

  • Ukuran Variabel
  • Akses Lambat di tengah - O (n)
    • Perlu untuk melintasi setiap elemen mulai dari kepala untuk mencapai indeks yang diinginkan
  • Akses Cepat di Head - O (1)
  • Akses yang berpotensi cepat di Tail
    • O (1) jika referensi disimpan di ujung ekor (seperti daftar yang ditautkan dua kali lipat)
    • O (n) jika tidak ada referensi yang disimpan (kompleksitas yang sama dengan mengakses sebuah simpul di tengah)

Daftar Array (seperti List<T>dalam C #!) Adalah campuran dari keduanya, dengan penambahan yang cukup cepat dan akses acak. List<T> akan sering menjadi koleksi masuk Anda ketika Anda tidak yakin apa yang harus digunakan.

  • Menggunakan array sebagai struktur pendukung
  • Pintar tentang mengubah ukurannya - mengalokasikan dua kali lipat dari ruang saat ini ketika kehabisan itu.
    • Ini menyebabkan O (log n) mengubah ukuran, yang lebih baik daripada mengubah ukuran setiap kali kami menambah / menghapus
  • Akses Cepat - O (1)

Bagaimana Array Bekerja

Kebanyakan bahasa memodelkan array sebagai data yang berdampingan dalam memori, di mana setiap elemen memiliki ukuran yang sama. Katakanlah kita memiliki array ints (ditampilkan sebagai [alamat: nilai], menggunakan alamat desimal karena saya malas)

[0: 10][32: 20][64: 30][96: 40][128: 50][160: 60]

Masing-masing elemen ini adalah integer 32-bit, jadi kita tahu berapa banyak ruang yang diperlukan dalam memori (32 bit!). Dan kita tahu alamat memori dari pointer ke elemen pertama.

Sangat mudah untuk mendapatkan nilai dari elemen lain dalam array itu:

  • Ambil alamat elemen pertama
  • Ambil offset masing-masing elemen (ukurannya dalam memori)
  • Lipat gandakan offset dengan indeks yang diinginkan
  • Tambahkan hasil Anda ke alamat elemen pertama

Katakanlah elemen pertama kita di '0'. Kita tahu elemen kedua kita di '32' (0 + (32 * 1)), dan elemen ketiga kita di 64 (0 + (32 * 2)).

Fakta bahwa kita dapat menyimpan semua nilai-nilai ini di satu sama lain dalam memori berarti array kita sangat kompak. Ini juga berarti bahwa semua elemen kita perlu tetap bersama untuk hal-hal untuk terus bekerja!

Segera setelah kita menambah atau menghapus elemen, kita perlu mengambil yang lainnya, dan menyalinnya ke tempat baru di memori, untuk memastikan tidak ada celah di antara elemen, dan semuanya memiliki ruang yang cukup. Ini bisa sangat lambat , terutama jika Anda melakukannya setiap kali Anda ingin menambahkan satu elemen.

Daftar Tertaut

Tidak seperti array, Linked Linked tidak perlu semua elemen mereka bersebelahan dalam memori. Mereka terdiri dari node, yang menyimpan info berikut:

Node<T> {
    Value : T      // Value of this element in the list
    Next : Node<T> // Reference to the node that comes next
}

Daftar itu sendiri menyimpan referensi ke kepala dan ekor (node ​​pertama dan terakhir) dalam banyak kasus, dan kadang-kadang melacak ukurannya.

Jika Anda ingin menambahkan elemen ke akhir daftar, yang perlu Anda lakukan adalah mendapatkan buntut , dan mengubahnya Nextuntuk referensi yang baru yang Nodemengandung nilai Anda. Menghapus dari ujung juga sama sederhana - cukup dereferensi Nextnilai dari node sebelumnya.

Sayangnya, jika Anda memiliki LinkedList<T>elemen dengan 1000, dan Anda ingin elemen 500, tidak ada cara mudah untuk melompat langsung ke elemen ke-500 seperti ada dengan array. Anda harus mulai dari kepala , dan terus ke Nextnode, sampai Anda melakukannya 500 kali.

Inilah sebabnya mengapa menambah dan menghapus dari a LinkedList<T>cepat (ketika bekerja di ujung), tetapi mengakses bagian tengah lambat.

Sunting : Brian menunjukkan dalam komentar bahwa Linked Linked memiliki risiko menyebabkan kesalahan halaman, karena tidak disimpan dalam memori yang berdekatan. Ini bisa sulit untuk diperbandingkan, dan dapat membuat Linked Linked bahkan sedikit lebih lambat daripada yang Anda harapkan hanya dengan kompleksitas waktu mereka.

Terbaik dari Kedua Dunia

List<T>kompromi untuk keduanya T[]dan LinkedList<T>dan muncul dengan solusi yang cukup cepat dan mudah digunakan dalam sebagian besar situasi.

Secara internal, List<T>adalah array! Itu masih harus melompat melalui lingkaran menyalin elemen-elemennya ketika mengubah ukuran, tetapi itu menarik beberapa trik yang rapi.

Sebagai permulaan, menambahkan elemen tunggal biasanya tidak menyebabkan array untuk menyalin. List<T>memastikan selalu ada cukup ruang untuk lebih banyak elemen. Ketika habis, alih-alih mengalokasikan array internal baru hanya dengan satu elemen baru, ia akan mengalokasikan array baru dengan beberapa elemen baru (seringkali dua kali lebih banyak dari yang saat ini dipegangnya!).

Operasi penyalinan mahal, jadi List<T>kurangi sebanyak mungkin, sambil tetap memungkinkan akses acak cepat. Sebagai efek samping, ini mungkin akan menghabiskan sedikit lebih banyak ruang daripada array langsung atau daftar tertaut, tetapi biasanya bernilai tradeoff.

TL; DR

Gunakan List<T>. Biasanya itu yang Anda inginkan, dan tampaknya benar untuk Anda dalam situasi ini (di mana Anda menelepon .Add ()). Jika Anda tidak yakin dengan apa yang Anda butuhkan, List<T>adalah tempat yang baik untuk memulai.

Array baik untuk kinerja tinggi, "Saya tahu saya perlu elemen X". Atau, mereka berguna untuk cepat, sekali-kali "Saya perlu mengelompokkan hal-hal X ini yang sudah saya definisikan bersama sehingga saya dapat mengulanginya" struktur.

Ada sejumlah kelas koleksi lainnya. Stack<T>seperti daftar tertaut yang hanya beroperasi dari atas. Queue<T>berfungsi sebagai daftar masuk pertama keluar pertama. Dictionary<T, U>adalah pemetaan asosiatif yang tidak tertata antara kunci dan nilai. Bermainlah dengan mereka dan kenali kekuatan dan kelemahan masing-masing. Mereka dapat membuat atau merusak algoritma Anda.

KChaloux
sumber
2
Dalam beberapa kasus, mungkin ada keuntungan menggunakan kombinasi array dan intmenunjukkan jumlah elemen yang dapat digunakan. Di antara hal-hal lain, dimungkinkan untuk menyalin banyak elemen sekaligus dari satu array ke array lainnya, tetapi menyalin di antara daftar umumnya membutuhkan elemen pemrosesan secara individual. Selanjutnya, elemen array dapat diteruskan refke hal-hal seperti Interlocked.CompareExchange, sementara item daftar tidak bisa.
supercat
2
Saya berharap saya bisa memberi lebih dari satu upvote. Saya tahu perbedaan use-case, dan bagaimana array / daftar yang terhubung bekerja, tetapi saya tidak pernah tahu atau berpikir tentang cara List<>kerja di bawah tenda.
Bobson
1
Menambahkan elemen tunggal ke Daftar <T> diamortisasi O (1); efisiensi penambahan elemen biasanya tidak cukup pembenaran untuk menggunakan daftar tertaut (dan Daftar melingkar memungkinkan Anda untuk menambahkan ke depan DAN kembali dalam diamortisasi O (1)). Daftar tertaut memiliki banyak keanehan kinerja. Sebagai contoh, tidak disimpan secara bersamaan dalam memori berarti mengulangi seluruh daftar yang tertaut lebih cenderung memicu kesalahan halaman ... dan ini sulit untuk dibandingkan. Pembenaran yang lebih besar untuk menggunakan Daftar Tertaut adalah ketika Anda ingin menggabungkan dua daftar (dapat dilakukan di O (1)) atau menambahkan elemen ke tengah.
Brian
1
Saya harus mengklarifikasi. Ketika saya mengatakan daftar bundar, maksud saya adalah daftar array melingkar, bukan daftar tertaut melingkar. Istilah yang benar adalah deque (antrian ujung ganda). Mereka sering diimplementasikan dengan cara yang hampir sama dengan List (array di bawah tenda), dengan satu pengecualian: Ada nilai integer internal, "pertama" yang menunjukkan indeks array mana yang merupakan elemen pertama. Untuk menambahkan elemen ke belakang, Anda cukup mengurangi 1 dari "pertama" (membungkus panjang array jika perlu). Untuk mengakses suatu elemen, Anda cukup mengakses (index+first)%length.
Brian
2
Ada beberapa hal yang tidak dapat Anda lakukan dengan Daftar yang dapat Anda lakukan dengan array biasa, misalnya, melewatkan elemen indeks sebagai parameter ref.
Ian Goldby
6

Sementara jawaban KChaloux bagus, saya ingin menunjukkan pertimbangan lain: List<T>jauh lebih kuat daripada Array. Metode List<T>sangat berguna dalam banyak keadaan - sebuah Array tidak memiliki metode ini dan Anda mungkin menghabiskan banyak waktu untuk mengimplementasikan solusi.

Jadi, dari perspektif pengembangan saya hampir selalu menggunakan List<T>karena ketika ada persyaratan tambahan, mereka sering jauh lebih mudah diimplementasikan ketika Anda menggunakan a List<T>.

Ini mengarah ke masalah terakhir: Kode saya (saya tidak tahu tentang Anda) mengandung 90% List<T>, jadi Array tidak benar-benar cocok. Ketika saya membagikannya, saya harus memanggil .toList()metode mereka dan mengubahnya menjadi Daftar - ini menjengkelkan dan sangat lambat sehingga keuntungan kinerja dari menggunakan Array hilang.

Sauer Kristen
sumber
Itu benar, ini adalah alasan bagus lainnya untuk menggunakan Daftar <T> - ini menyediakan lebih banyak fungsi untuk Anda buat langsung ke dalam kelas.
KChaloux
1
Tidakkah LINQ meratakan bidang dengan menambahkan banyak fungsi itu untuk setiap IEnumerable (termasuk sebuah array)? Apakah ada yang tersisa di C # (4+) modern yang hanya dapat Anda lakukan dengan Daftar <T> dan bukan array?
Dave
1
@Dave Memperluas Array / Daftar sepertinya hal seperti itu. Juga, saya akan mengatakan sintaks untuk membangun / menangani Daftar jauh lebih baik daripada untuk array.
Christian Sauer
2

Tidak ada yang menyebutkan bagian ini: "Dan di sini saya sudah mendapatkan Pengecualian Kehabisan Memori." Yang sepenuhnya karena

for (int i = 0; i < ItemNumberList.Count; i++)
{
    ItemNumberList.Add(new item());
}

Jelas untuk melihat alasannya. Saya tidak tahu apakah Anda bermaksud menambahkan ke daftar yang berbeda, atau hanya menyimpan ItemNumberList.Countsebagai variabel sebelum loop untuk mendapatkan hasil yang Anda inginkan, tetapi ini hanya rusak.

Programmers.SE adalah untuk "... tertarik dengan pertanyaan konseptual tentang pengembangan perangkat lunak ...", dan jawaban lain memperlakukannya seperti itu. Coba http://codereview.stackexchange.com , di mana pertanyaan ini cocok. Tetapi meskipun ada yang mengerikan, karena kita hanya dapat mengasumsikan kode ini dimulai _Click, yang tidak memiliki panggilan ke multiValue1tempat Anda mengatakan kesalahan terjadi.

Wolfzoon
sumber