Startseite  Index  <<  >>  

10 - Arrays

Angenommen, Sie möchten mehrere Werte vom gleichen Typ in einer Anwendung verwalten. Wenn Sie dies über mehrere Variablen durchführen wollen, kann dies bei 5 oder mehr Werten schon lästig sein. Noch unangenehmer wird es, wenn Sie die Anzahl der zu verwaltenden Daten nicht kennen. Genau aus diesem Grund existiert in den meisten Programmiersprachen wie auch in C# die Möglichkeit, ein so genanntes Array zu definieren. Ein solches Array kann mehrere Werte eines bestimmten Typs aufnehmen. Neben eindimensionalen Arrays können Sie auch mehrdimensionale Arrays definieren. So lassen sich z.B. über zweidimensionale Arrays die Daten wie in einer Tabelle verwalten.

Beispiel: Mit Arrays mehrere Zahlen speichern

Es wird ein Array von zehn int-Elementen erzeugt. Danach werden die einzelnen Array-Elemente mit Werten versehen. In eckigen Klammern wird der Null-basierte Index des jeweiligen Array-Elements angegeben.
int[] zahlen = new int[10];
for(int i = 0;  i < 10; i++)
  zahlen[i] = (i+1) * (i+1);

10.1 Eindimensionale Arrays

Diese Arrays verwalten innerhalb einer Datenstruktur eine Menge von Daten, die über einen Index angesprochen werden. Innerhalb dieser Datenstruktur werden Elemente verwaltet, die alle vom gleichen Typ sind.
Syntax
  • Geben Sie zuerst den Datentyp der zu verwaltenden Elemente gefolgt von einem eckigen Klammerpaar [] an. Im Gegensatz zu anderen Programmiersprachen muss das Klammerpaar immer direkt hinter dem Datentyp angegeben werden.
  • Jetzt folgt durch ein Leerzeichen getrennt der Bezeichner für das Array.
  • Durch ein Gleichheitszeichen getrennt kann ein Array über new erzeugt werden. Geben Sie dazu den Datentyp und in eckigen Klammern die Größe des Arrays an. Die Größe kann auch über den Wert einer Variablen festgelegt werden. In dieser Hinsicht wird die Größe eines Arrays also auch dynamisch festgelegt.
  • Geben Sie alternativ zu new in geschweiften Klammern die Elemente des Arrays an. In diesem Fall wird von einem Array-Initialisierer gesprochen, der auch gleichzeitig die Größe des Arrays festlegt.
  • Um ein Array-Element anzusprechen, geben Sie den Bezeichner und danach in eckigen Klammern den Index des Elements an. Der Index beginnt immer bei 0. Das letzte Element eines 10-elementigen Arrays besitzt also beispielsweise den Index 9.
  • Die Elemente eines Arrays werden im Falle von Zahlen mit 0, im Falle von Referenztypen mit null initialisiert.
  • Arrays werden implizit von der Klasse Array aus dem Namespace System erweitert, implementieren das Interfaces IEnumerable und können deshalb über eine foreach-Schleife durchlaufen werden (mehr dazu auch im Kapitel zu den Auflistungen). Von der Klasse Array können Sie keine eigenen Klassen ableiten, dies ist nur dem Compiler erlaubt.
  • Haben Sie einmal die Größe eines Arrays festgelegt, lässt sich diese nicht mehr ändern. Sie können nur ein neues Arrays mit einer neuen Größe deklarieren und die Elemente des alten Arrays in dieses Array übertragen. Alternativ verwenden Sie Auflistungen, die in dieser Hinsicht flexibler sind.
  • Da Arrays Referenztypen sind, werden sie auch später durch den Garbage Collector entsorgt.

    Wenn Sie ein Array deklarieren und davon ein Objekt erstellen, so besitzt dieses Objekt zahlreiche Eigenschaften und Methoden, die es von der Klasse Array geerbt hat. So lässt sich z.B. die Länge eines Arrays ermitteln oder die Anzahl der Dimensionen bestimmen.

    Eigenschaft / Methode Beschreibung
    IsFixedSize
    IsReadOnly
    Die Eigenschaft IsFixedSize liefert bei Arrays immer den Wert true, während IsReadOnly immer false liefert. Das heißt, ein Array hat immer eine feste Größe und seine Elemente können immer bearbeitet werden. Beide Eigenschaften müssen aufgrund der von der Klasse Array implementierten Schnittstelle IList bereitgestellt werden.
    LengthLiefert die Länge eines Arrays aller Dimensionen. Erzeugen Sie ein zweidimensionales Array der Größe 3 x 4 erhalten Sie z.B. 12 als Rank.
    RankLiefert die Anzahl der Dimensionen zurück.
    Clone()Erstellt eine so genannte flache Kopie eines Arrays. Dabei werden die Elemente kopiert, unabhängig davon, ob es Wert- oder Referenztypen sind. Dies hat die Konsequenz, dass die Referenzen auf die gleichen Objekte wie die des ersten Arrays zeigen.
    GetLength()Liefert die Größe der im Parameter angegebenen Dimension zurück. Damit können Sie gegenüber der Eigenschaft Length auch die Größe von einzelnen Dimensionen in mehrdimensionalen Arrays bestimmen.
    Tabelle 10.1: Ausgewählte Eigenschaften und Methoden eines Arrays

    Beispiel: Arrays deklarieren, initialisieren und auslesen

    Es werden nun alle Möglichkeiten vorgestellt, ein Array zu deklarieren, zu erzeugen und zu initialisieren. Das Array zahlen1 wird in einer Zeile deklariert und erzeugt. Es kann drei int-Werte aufnehmen. Auf die Elemente eines Arrays wird über den Index zugegriffen und ein passender Wert wird zugewiesen.

    Die Arrays zahlen2 und zahlen3 verwenden einen (Array-)Initialisierer, der die Größe vorgibt und die Elemente auch gleich initialisiert. Die Verwendung eines Initialisierers ist nur auf diese Weise möglich. Bei einer getrennten Deklaration und Erzeugung wie beim Array zahlen4 können Sie bei der Erzeugung keine Initialisierung mehr vornehmen.

    Zum Durchlaufen eines Arrays können Sie in einer for-Schleife die Eigenschaft Length als obere Grenze verwenden. Die Verwendung einer for-Schleife hat den Vorteil, dass Sie den Index des Elements weiterverwenden können. Verwenden Sie die foreach-Schleife, geht die Indexinformation verloren.

    Zum Abschluss wird noch die Anzahl der Dimensionen zurückgegeben, die hier 1 ist.

    using System;
    namespace CSharpBuch.Kap10
    {
      public class EindimensionaleArrays
      {
        static void Main(string[] args)
        {
          int[] zahlen1 = new int[3];
          zahlen1[0] = 2;
          zahlen1[1] = 3;
          zahlen1[2] = 5;
    
          int[] zahlen2 = { 2, 3, 5 };
          int[] zahlen3 = new int[] { 2, 3, 5 };
    
          int[] zahlen4;
          zahlen4 = new int[3];
    
          for(int i = 0; i < zahlen1.Length; i++)
            Console.WriteLine("Zahl {0}: {1}", i, zahlen1[i]);
          foreach(int zahl in zahlen1)
            Console.WriteLine("Zahl: {0}", zahl);
          Console.WriteLine("Dimensionen: {0}", zahlen1.Rank);
        }
      }
    }
    Listing 10.1: EindimensionaleArrays.cs

    INFO

    Da die hier vorgestellten Arrays immer nur den Datentyp int, also einen Werttyp, verwendet haben, wurde im Array auch Platz für einen solchen Typ bereitgestellt. Wenn Sie dagegen ein Array von Referenztypen erstellen, z.B. Person[], wird nur Platz für die Referenzen bereitgestellt. Die Objekte müssen Sie für jedes Element separat erstellen. Dazu folgt noch ein Beispiel.

    10.2 Mehrdimensionale Arrays

    In mehrdimensionalen Arrays können Sie z.B. Daten wie in einer Tabelle oder einer Matrix speichern (im Falle eines zweidimensionalen Arrays).

    Syntax
  • Geben Sie den Typ gefolgt von einem eckigen Klammerpaar an. Für jede weitere Dimension, geben Sie in den Klammern ein Komma an.
  • Zur Erzeugung des Arrays geben Sie in den eckigen Klammern für jede Dimension einen Wert an. Diese werden durch Komma getrennt. Sie müssen sofort einen Wert für jede Dimension angeben.
  • Statt der Angabe der Dimensionen können Sie die Erzeugung und Initialisierung auch wieder miteinander verbinden. Geben Sie für jede Dimension die Daten in einem Klammerpaar {} an. Trennen Sie die Klammerpaare mit einem Komma.
  • Mehrdimensionale Arrays haben feste Dimensionen, d.h., ein dreidimensionales Array der Größe 2x3x3 besteht aus 18 Elementen.
  • Zum Zugriff auf mehrdimensionale Arrays werden die Indizes, durch Komma getrennt, für jede Dimension angegeben.
  • Die einzelnen Dimensionen sind alle vom gleichen Datentyp. Wenn Sie beliebige Typen im Array verwalten wollen, müssen Sie ein Array mit object-Elementen erzeugen und diese beim Entnehmen wieder in den korrekten Datentyp casten.

    Beispiel: Daten in Tabellenform verwalten

    Zuerst werden die möglichen Varianten zur Deklaration, Erzeugung und Initialisierung eines zweidimensionalen Arrays gezeigt. Wie im Falle des Arrays matrix3 sind auch dynamische Größenangaben über Variablen möglich. Damit Sie die Größen der Dimensionen dynamisch ermitteln können, verwenden Sie die Methode GetLength() unter Angabe der Dimension.

    Zum Abschluss werden die Größen der einzelnen Dimensionen des Arrays ausgegeben.

    using System;
    namespace CSharpBuch.Kap10
    {
      public class MehrdimensionaleArrays
      {
        static void Main(string[] args)
        {
          int[,] matrix1 = new int[2,3];
          int[,] matrix2 = new int[2, 3] { {2, 4, 6}, {1, 3, 5} };
    
          int zeilen = 3;
          int spalten = 10;
          int[,] matrix3 = new int[zeilen, spalten];
    
          for(int i = 0; i < matrix2.GetLength(0); i++)
            for(int j = 0; j < matrix2.GetLength(1); j++)
              Console.WriteLine("{0},{1}: {2}", i, j, matrix2[i, j]);
          for(int dim = 0; dim < matrix2.Rank; dim++)
            Console.WriteLine("Größe der Dimension {0}: {1}", dim, matrix2.GetLength(dim));
         }
      }
    }
    Listing 10.2: MehrdimensionaleArrays.cs

    INFO

    Auch bei mehrdimensionalen Arrays lässt sich die foreach-Schleife verwenden. Diese liefert aber einfach alle Elemente des Arrays, unabhängig von ihrer Position, zurück. Wenn Sie die konkreten Indizes benötigen, ist eine verschachtelte for-Schleife wie im Beispiel vorzuziehen.

    10.3 Verzweigte Arrays

    Diese Art der Arrays (auch unregelmäßige Arrays, Arrays von Arrays oder jagged Arrays genannt) besteht nicht aus einer festen Anzahl von Elementen pro Dimension, sondern die Anzahl kann variieren.
    Syntax
  • Geben Sie den Datentyp und für jede Dimension ein eckiges Klammerpaar an (im Unterschied zu "normalen" mehrdimensionalen Arrays, deren Dimensionen durch Komma getrennt werden).
  • Erzeugen Sie jede Dimension einzeln, indem Sie hinter new ein eckiges Klammerpaar, darin die Größe der betreffenden Dimension und nachfolgend für alle weiteren Dimensionen leere Klammerpaare angeben.
  • Alternativ können Sie auch Initialisiererlisten verwenden.

    Beispiel: Verschiedene Initialisierungsformen verzweigter Arrays

    Jede Dimension muss separat erzeugt werden. Nachdem die erste Dimension erzeugt wurde, wird dann nach und nach die Größe jeder weiteren Dimension individuell festgelegt. Dabei kann die Größenangabe wieder mit einer Initialisierung verbunden werden.

    int[][] verzw = new int[3][];
    verzw[0] = new int[] { 1, 2, 3};

    Alternativ können Sie auch alle Dimensionen initialisieren. Dazu muss pro Dimension wieder new int[] angegeben werden, gefolgt von den konkreten Werten.

    int[][]verzw = new int[][] 
    {
      new int[] {1, 2, 3}, 
      new int[] {4, 5}
    };

    Die Angabe new int[][] kann auch weggelassen werden.

    int[][] verzw =
    {
      new int[] {1, 2, 3}, 
      new int[] {4, 5}
    };

    Als Krönung lassen sich auch "normale" Arrays mit verzweigten Arrays kombinieren. Allerdings führen solche Deklarationen später nicht unbedingt zu lesbaren Code, so dass Sie solche Konstrukte eher sparsam oder gar nicht einsetzen sollten.

    int[][,] = new int[4][,];

    Beispiel: Matrizenrechnung mit verzweigten Arrays

    Dieses Beispiel erzeugt eine rechteckige Matrix, füllt sie mit Werten und gibt diese dann auf der Konsole aus. Im Gegensatz zu "normalen" mehrdimensionalen Arrays können Sie in verzweigten Arrays in jeder Dimension die Eigenschaft Length zur Größenbestimmung heranziehen. Der Zugriff auf die Elemente erfolgt pro Dimension über ein eigenes rechteckiges Klammerpaar und der Angabe eines Null-basierten Index.
    using System;
    namespace CSharpBuch.Kap10
    {
      public class VerzweigteArrays
      {
        static void Main(string[] args)
        {
          int[][] dreieck = new int[3][];
          
          for(int i = 1; i <= 3; i++)
            dreieck[i - 1] = new int[i];
    
          for(int i = 0; i < dreieck.Length; i++)
            for(int j = 0; j < dreieck[i].Length; j++)
              dreieck[i][j] = j + 1;
    
          for(int i = 0; i < dreieck.Length; i++)
          {
            for(int j = 0; j < dreieck[i].Length; j++)
              Console.Write("{0}\t", dreieck[i][j]);
            Console.WriteLine();
          }
        }
      }
    }
    Listing 10.3: VerzweigteArrays.cs

    Ausgabe:
    1
    1       2
    1       2       3

    10.4 Parameterübergabe an Methoden

    Arrays können, wie andere Typen auch, an Methoden als Parameter übergeben werden. Dabei ist die Übergabe als Wert, Referenz oder Ausgabeparameter möglich. Da Arrays Referenztypen sind, lassen sich über den Parameter auch die Werte der Felder ändern. Bei der Übergabe per ref- oder out-Parameter können Sie dem Parameter in der Methode z.B. ein neues Array zuweisen. Außerdem können Sie ein Array direkt beim Aufruf der Methode erzeugen und initialisieren, wenn Sie später keinen Zugriff mehr darauf benötigen.

    Beispiel: Arrays als Parameter

    Die Methode Summe() erwartet ein Array von int-Werten als Parameter. Zum Aufruf der Methode kann nun ein konkretes Array wie Werte verwendet werden oder Sie kombinieren die Erstellung und Initialisierung bei der Parameterübergabe wie beim zweiten Aufruf von Summe().
    int Summe(int[] werte)
    {
      int ergebnis = 0;
      foreach(int summand in werte)
        ergebnis += summand;
      return ergebnis;
    }
    ...
    int[] werte = new int[] {1, 2, 3, 4, 5};
    int ergebnis = Summe(werte);
    int ergebnis = Summe(new int[] {1, 2, 3, 4, 5});

    Beispiel: Parameterübergabe von Arrays als Wert, Referenz und Ausgabeparameter

    Die Anwendung verwendet drei Methoden, um die Werte eines Arrays zu initialisieren (Init(), Ausgabeparameter), die Werte zu modifizieren (Change(), Wertübergabe) und die Reihenfolge zu vertauschen (Reverse(), Referenzparameter). Zum Abschluss werden die Werte aus dem Array noch auf der Konsole ausgegeben.
    using System;
    namespace CSharpBuch.Kap10
    {
      public class ArrayParameter
      {
        static void Main(string[] args)
        {
          int[] daten;
          Init(out daten);
          Change(daten);
          Reverse(ref daten);
          for(int i = 0; i < daten.Length; i++)
            Console.WriteLine("Wert {0}: {1}", i, daten[i]);
        }
        static void Init(out int[] daten)
        {
          daten = new int[] { 1, 2, 3 };
        }
        static void Change(int[] daten)
        {
          for(int i = 0; i < daten.Length; i++)
            daten[i] = daten[i] * 2;
        }
        static void Reverse(ref int[] daten)
        {
          Array.Reverse(daten);
        }
      }
    }
    Listing 10.4: ArrayParameter.cs

    10.5 Statische Methoden der Klasse Array

    Neben den Methoden, die Arrays bereits von der Klasse Array erben, besitzt diese Klasse noch einige sehr nützliche statische Methoden, die auf ein Array angewendet werden können. Die Methoden besitzen zum Teil zahlreiche Überladungen, so dass ein Blick in die Hilfe empfohlen wird, wenn Sie diese nutzen möchten.

    Einige dieser Methoden nutzen die Funktionalität von Generics, andere benötigen einen Delegaten, um eine bestimmte Operation auszuführen. Beide Themen werden später noch in eigenen Kapiteln behandelt - das bekannte Problem mit dem Huhn und dem Ei.

    Eigenschaft / MethodeBeschreibung
    BinarySearch()Durchsucht ein sortiertes Array nach einem Objekt. Zum Vergleich wird die Methode CompareTo() verwendet.
    Clear()Setzt den angegebenen Bereich auf 0 bzw. null.
    Copy()Kopiert einen Teil eines Arrays in ein anderes Array.
    ForEach()Führt eine Operation mit jedem Array-Element aus.
    IndexOf()
    LastIndexOf()
    Liefert den Index des ersten/letzten Vorkommens eines Wertes im Array.
    Resize()Erstellt ein neues Array und überträgt die Elemente des Ausgangs-Arrays. Wird das Array verkleinert, fallen überflüssige Elemente unter den Tisch.
    Reverse()Kehrt die Reihenfolge der Elemente im Array um.
    Sort()Sortiert die Elemente im Array.
    TrueForAll()Prüft, ob alle Array-Elemente eine bestimmte Bedingung erfüllen.
    Tabelle 10.2: Ausgewählte statische Methoden der Klasse Array

    Beispiel: Arrays von benutzerdefinierten Typen sortieren

    Die Klasse Person verwaltet den Namen und das Alter einer Person. Außerdem besitzt sie eine Methode CompareTo(), um zwei Person-Objekte miteinander vergleichen zu können (hier nur über den Namen). Von dieser Klasse wird jetzt ein Array erzeugt und mit vier Objekten initialisiert. Diese werden gleich bei der Erzeugung des Arrays mit angegeben.

    Über die Methode ForEach() der Klasse Array werden die Urlaubsansprüche der Personen ausgegeben. Diese Funktionalität ließe sich natürlich auch direkt implementieren. Durch die Verwendung der Methode ForEach() können Sie aber sehr einfach die Methode austauschen (durch Übergabe eines anderen Delegaten - siehe Kapitel 13), welche auf die Array-Elemente angewandt wird. Im konkreten Fall heißt dass, Sie können die Methode austauschen, die den Urlaubsanspruch berechnet (z.B. bei neuen Tarifverträgen).

    Über die Methode Sort() werden dann die Elemente des Arrays sortiert. Aus diesem Grund wurde in der Klasse Person auch das Interface IComparable implementiert, da die Sortierung sonst nicht auf diese Weise möglich wäre.

    Beachten Sie, dass die Daten in der Klasse Person nur aus Gründen der Einfachheit als public deklariert wurden. Verwenden Sie sonst besser Eigenschaften und private Variablen.
    using System;
    namespace CSharpBuch.Kap10
    {
      public class ArrayFunktionen
      {
        static void Main(string[] args)
        {
          Person[] personen = new Person[]
          { 
            new Person("Meier", 25),
            new Person("Schulze", 35),
            new Person("Krause", 45),
            new Person("Duck", 30)
          };
          Array.ForEach(personen, AlterFilter);
    
          Array.Sort(personen);
          foreach(Person p in personen)
            Console.WriteLine(p.Name);
        }
        private static void AlterFilter(Person p)
        {
          if(p.Alter < 35)
            Console.WriteLine("{0} hat 28 Tage Urlaub", p.Name);
          else
            Console.WriteLine("{0} hat 30 Tage Urlaub", p.Name);
        }
      }
      public class Person: IComparable
      {
        public string Name;
        public int Alter;
        public Person(string name, int alter)
        {
          Name = name;
          Alter = alter;
        }
        public int CompareTo(object obj)
        {
          return Name.CompareTo((obj as Person).Name);
        }
      }
    }
    Listing 10.5: ArrayFunktionen.cs

    10.6 Übungsaufgaben

    Aufgabe 1
    Erstellen Sie eine Methode MaxWert(), der ein Array von int-Werten übergeben wird. Die Methode liefert als Ergebnis den größten gefunden Wert aus dem Array zurück. Testen Sie Ihre Anwendung.
    Aufgabe 2
    Deklarieren Sie ein verzeigtes, zweidimensionales Array von int-Werten, das in der ersten Dimension aus drei Elementen besteht. Initialisieren Sie diese drei "Unterarrays" mit verschiedenen Längen und initialisieren Sie diese. Übergeben Sie jetzt diese Unterarrays der Methode MaxWert() aus Aufgabe 1. Auf diese Weise können Sie verschiedene Testreihen für diese Methode erzeugen, die nicht immer dieselbe Länge besitzen.
    Aufgabe 3
    Erstellen Sie eine Anwendung, in der drei Zufallszahlen mit den Werten zwischen 1 und 5 (Klasse Random, Methode Next()) in einem Array abgelegt werden sollen. Prüfen Sie über die Methode TrueForAll() der Klasse Array, ob alle drei Zahlen den Wert 3 besitzen. Zählen Sie die Anzahl der Testreihen, bis TrueForAll() das Ergebnis true liefert, und geben Sie diese Anzahl aus.