it-swarm.com.de

Soll ich eine Liste oder ein Array verwenden?

Ich arbeite an einem Windows-Formular, um UPC für Artikelnummern) zu berechnen.

Ich habe erfolgreich eine erstellt, die jeweils eine Artikelnummer/UPC verarbeitet. Jetzt möchte ich sie erweitern und für mehrere Artikelnummern/UPCs ausführen.

Ich habe angefangen und versucht, eine Liste zu verwenden, aber ich stecke immer wieder fest. Ich habe eine Hilfsklasse erstellt:

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

}

Dann habe ich mit meinem Code begonnen, aber das Problem ist, dass der Prozess inkrementell ist, was bedeutet, dass ich die Artikelnummer über Kontrollkästchen aus einer Rasteransicht erhalte und sie in die Liste einfüge. Dann bekomme ich das letzte UPC aus der Datenbank, entferne das Häkchen, erhöhe die Zahl um eins und setze sie in die Liste ein. Dann berechne ich das Häkchen für die neue Zahl und setze das in die Liste. Und hier bekomme ich bereits eine Ausnahme wegen Speichermangel. Hier ist der Code, den ich bisher habe:

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, "", ""));
        }

Ist dies der richtige Weg, um eine Liste zu verwenden, oder sollte ich einen anderen Weg einschlagen?

22
campagnolo_1

Ich werde meinen Kommentar erweitern:

... wenn Sie Hinzufügen oder Entfernen Elemente sind, möchten Sie eine Liste (oder eine andere flexible Datenstruktur). Arrays sind nur dann wirklich gut, wenn Sie genau wissen, wie viele Elemente Sie zu Beginn benötigen.

Eine schnelle Aufschlüsselung

Arrays sind gut, wenn Sie ein feste Anzahl von Elementen haben, das sich wahrscheinlich nicht ändert, und Sie möchten nicht sequentiell darauf zugreifen.

  • Feste Größe
  • Schneller Zugriff - O (1)
  • Slow Resize - O(n) - muss jedes Element in ein neues Array kopieren!

Linked-Lists sind für schnelles Hinzufügen und Entfernen an beiden Enden optimiert, haben jedoch in der Mitte nur langsamen Zugriff.

  • Variable Größe
  • Langsamer Zugriff in der Mitte - O (n)
    • Muss jedes Element vom Kopf aus durchlaufen, um den gewünschten Index zu erreichen
  • Schneller Zugang an der Spitze - O (1)
  • Potenziell schneller Zugang am Heck
    • O(1) if a reference is stored at the tail end (as with a doubly-linked list)
    • O(n) if no reference is stored (same complexity as accessing a node in the middle)

Array-Listen (wie List<T> In C #!) Sind eine Mischung aus beiden mit relativ schnellen Ergänzungen nd Direktzugriff. List<T>wird oft Ihre erste Sammlung sein, wenn Sie nicht sicher sind, was Sie verwenden sollen.

  • Verwendet ein Array als Hintergrundstruktur
  • Ist schlau in Bezug auf die Größenänderung - weist das Doppelte seines aktuellen Speicherplatzes zu, wenn es knapp wird.
    • Dies führt zu einer Größenänderung von O (log n). Dies ist besser als die Größenänderung bei jedem Hinzufügen/Entfernen
  • Schneller Zugriff - O (1)

Wie Arrays funktionieren

Die meisten Sprachen modellieren Arrays als zusammenhängende Daten im Speicher, von denen jedes Element dieselbe Größe hat. Angenommen, wir hatten ein Array von ints (angezeigt als [Adresse: Wert], wobei Dezimaladressen verwendet werden, weil ich faul bin).

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

Jedes dieser Elemente ist eine 32-Bit-Ganzzahl, sodass wir wissen, wie viel Speicherplatz es im Speicher beansprucht (32 Bit!). Und wir kennen die Speicheradresse des Zeigers auf das erste Element.

Es ist trivial einfach, den Wert eines anderen Elements in diesem Array zu ermitteln:

  • Nimm das Adresse des ersten Elements
  • Nehmen Sie das Offset jedes Elements (seine Größe im Speicher)
  • Multiplizieren der Versatz um den gewünschten Index
  • Fügen Sie Ihr Ergebnis zur Adresse des ersten Elements hinzu

Nehmen wir an, unser erstes Element ist bei '0'. Wir wissen, dass unser zweites Element bei '32' (0 + (32 * 1)) und unser drittes Element bei 64 (0 + (32 * 2)) liegt.

Die Tatsache, dass wir alle diese Werte nebeneinander im Speicher speichern können, bedeutet, dass unser Array so kompakt wie möglich ist. Es bedeutet auch, dass alle unsere Elemente bleiben zusammen sein müssen, damit die Dinge weiter funktionieren!

Sobald wir ein Element hinzufügen oder entfernen, müssen wir alles andere aufnehmen und an eine neue Stelle im Speicher kopieren, um sicherzustellen, dass keine Lücken zwischen den Elementen bestehen und alles genügend Platz hat. Dies kann sehr langsam, sein, insbesondere , wenn Sie dies jedes Mal tun, wenn Sie ein einzelnes Element hinzufügen möchten.

Verknüpfte Listen

Im Gegensatz zu Arrays müssen verknüpfte Listen nicht alle Elemente im Speicher nebeneinander haben. Sie bestehen aus Knoten, die folgende Informationen speichern:

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

Die Liste selbst enthält in den meisten Fällen einen Verweis auf head und tail (erster und letzter Knoten) und verfolgt manchmal deren Größe.

Wenn Sie am Ende der Liste ein Element hinzufügen möchten, müssen Sie nur das tail abrufen und dessen Next ändern, um auf ein neues Node zu verweisen mit Ihrem Wert. Das Entfernen vom Ende ist ebenso einfach - dereferenzieren Sie einfach den Next -Wert des vorhergehenden Knotens.

Wenn Sie ein LinkedList<T> Mit 1000 Elementen haben und Element 500 möchten, gibt es leider keine einfache Möglichkeit, direkt zum 500. Element zu springen, wie dies bei einem Array der Fall ist. Sie müssen am head beginnen und weiter zum Knoten Next gehen, bis Sie dies 500 Mal getan haben.

Aus diesem Grund ist das Hinzufügen und Entfernen von LinkedList<T> Schnell (wenn an den Enden gearbeitet wird), der Zugriff auf die Mitte ist jedoch langsam.

Bearbeiten: Brian weist in den Kommentaren darauf hin, dass verknüpfte Listen das Risiko haben, einen Seitenfehler zu verursachen, da sie nicht im zusammenhängenden Speicher gespeichert werden. Dies kann schwierig zu bewerten sein und verknüpfte Listen aufgrund ihrer zeitlichen Komplexität sogar etwas langsamer machen, als Sie vielleicht erwarten.

Beste aus beiden Welten

List<T> Geht sowohl für T[] Als auch für LinkedList<T> Kompromisse ein und bietet eine Lösung, die in den meisten Situationen relativ schnell und einfach zu verwenden ist.

Intern ist List<T> Ein Array! Es muss immer noch durch die Rahmen springen, um seine Elemente beim Ändern der Größe zu kopieren, aber es zieht einige nette Tricks.

Für den Anfang führt das Hinzufügen eines einzelnen Elements normalerweise nicht zum Kopieren des Arrays. List<T> Stellt sicher, dass immer genug Platz für mehr Elemente vorhanden ist. Wenn ein neues internes Array nur mit einem neuen Element zugewiesen wird, wird ein neues Array mit mehreren neuen Elementen zugewiesen (häufig doppelt so viele wie derzeit) hält!).

Kopiervorgänge sind teuer, daher reduziert List<T> Sie so weit wie möglich und ermöglicht dennoch einen schnellen Direktzugriff. Als Nebeneffekt verschwendet es möglicherweise etwas mehr Platz als ein direktes Array oder eine verknüpfte Liste, aber normalerweise lohnt sich der Kompromiss.

TL; DR

Verwenden Sie List<T>. Es ist normalerweise das, was Sie wollen, und es scheint in dieser Situation (in der Sie .Add () aufrufen) für Sie richtig zu sein. Wenn Sie sich nicht sicher sind, was Sie brauchen, ist List<T> Ein guter Anfang.

Arrays eignen sich gut für leistungsstarke Dinge wie "Ich weiß, ich brauche genau X Elemente". Alternativ sind sie nützlich für schnelle, einmalige Strukturen "Ich muss diese X-Dinge, die ich bereits definiert habe, gruppieren, damit ich sie durchlaufen kann".

Es gibt eine Reihe anderer Sammlungsklassen. Stack<T> Ist wie eine verknüpfte Liste, die nur von oben funktioniert. Queue<T> Funktioniert als First-In-First-Out-Liste. Dictionary<T, U> Ist eine ungeordnete assoziative Zuordnung zwischen Schlüsseln und Werten. Spielen Sie mit ihnen und lernen Sie die Stärken und Schwächen der einzelnen kennen. Sie können Ihre Algorithmen erstellen oder unterbrechen.

73
KChaloux

Während KChaloux Antwort großartig ist, möchte ich auf eine andere Überlegung hinweisen: List<T> Ist viel leistungsfähiger als ein Array. Die Methoden von List<T> Sind unter vielen Umständen sehr nützlich. Ein Array verfügt nicht über diese Methoden, und Sie verbringen möglicherweise viel Zeit mit der Implementierung von Problemumgehungen.

Aus entwicklungspolitischer Sicht verwende ich also fast immer List<T>, Da zusätzliche Anforderungen häufig viel einfacher zu implementieren sind, wenn Sie einen List<T> Verwenden.

Dies führt zu einem letzten Problem: Mein Code (ich weiß nichts über Ihren) enthält 90% List<T>, Daher passen Arrays nicht wirklich hinein. Wenn ich sie weitergebe, muss ich ihre .toList() Methode und konvertieren Sie sie in eine Liste - dies ist ärgerlich und so langsam, dass jeglicher Leistungsgewinn durch die Verwendung eines Arrays verloren geht.

6
Christian Sauer

Niemand erwähnte diesen Teil jedoch: "Und hier bekomme ich bereits eine Ausnahme wegen Speichermangels." Welches ist ganz auf

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

Es ist klar zu sehen warum. Ich weiß nicht, ob Sie beabsichtigten, einer anderen Liste hinzuzufügen, oder ob Sie nur ItemNumberList.Count Als Variable vor der Schleife speichern sollten, um das gewünschte Ergebnis zu erhalten, aber dies ist einfach fehlerhaft.

Programmers.SE ist für "... interessiert an konzeptionellen Fragen zur Softwareentwicklung ...", und die anderen Antworten behandelten es als solche. Versuchen Sie stattdessen http://codereview.stackexchange.com , wo diese Frage passen würde. Aber selbst dort ist es schrecklich, da wir nur annehmen können, dass dieser Code bei _Click Beginnt, der keinen Aufruf von multiValue1 Hat, wo Sie sagen, dass der Fehler auftritt.

2
Wolfzoon