» Tutorial / Java Grundlagen / Arrays

An dieser Stelle unseres Tutorials beschäftigen wir uns mit einem speziellen Typ der Variablen der Java - Spezifikation. Das Array stellt in Java einen wichtigen Aspekt in der Verwaltung typgleicher Variablen und Objekte dar. Dazu werden wir Ihnen alle wichtigen Konzepte im Umgang mit diesen Feldern vermitteln.


» Arrays als Container nach oben «

Zunächst einmal gilt es, die Frage zu klären, was genau man sich unter einem Array vorzustellen hat. Bei einem Array handelt es sich um ein Feld oder Container, das als Variable dazu in der Lage ist, mehrere Objekte vom gleichen Typ aufzunehmen und zu verwalten.

Dies erspart oft viel Arbeit, wenn es darum geht, mehrere Variblen gleichzeitig und komfortabel zu manipulieren. Dabei sind die in einem Array enthaltenen Variablen speziellen Eigenschaften und Zugriffsmöglichkeiten unterworfen.


» Speichermanagement nach oben «

Alle in einem Feld angelegten Variblen müssen natürlich auch im Arbeitsspeicher angelegt werden. Wenn wir uns erinnern, so wurden Variablen nur auf dem Stack allokiert, aber nie auf dem Arbeitsspeicher. Dies ist ein wichtiger Unterschied zu Arrays, denn diese nutzen immer den Arbeitsspeicher.

Dies liegt darin begründet, das Arrays in Java eigentlich keine eigenen Datentypen sind, sondern durch eine interne Klasse von Java repräsentiert werden. Da Java seine Klassen immer dynamisch auf dem Speicher anlegt, gilt dies auch für die Elemente der Klassen, in diesem Fall für die Variablen innerhalb des Arrays.

Die folgende Grafik verdeutlicht das Speichermanagement und die Anordnung der Containerelemente im Speicher. Wie zu sehen ist, so werden diese möglichst sequentiell angeordnet, um einen möglichst schnellen Zugriff auf alle Elemente zu ermöglichen.

Speichermanagement eines Arrays

Wie wir der Grafik entnehmen können, so beginnt das Array ab einer beliebigen Speicherposition. Je nach Größe der Elemente berechnet sich die nächste Position aus der Nummer des Elements mit der Bytegröße. Prinzipiell kann ein Array auch als eine Verkettung von Variablen gleichen Typs betrachtet werden.

Der letzte Aspekt zu diesem Teil betrachtet den Speicherverbrauch des Arrays. Dieser errechnet sich aus der Anzahl der Elemente und ihrer Bytegröße im Speicher.


» Implementierung in Java nach oben «

Wir hatten es bereits erwähnt. Arrays werden in Java durch eine spezielle Klasse repräsentiert. Dies ist nicht nur Vorteilhaft im Speichermanagement, sondern eröffnet auch ganz neue Möglichkeiten.

So kann ein Array sogar spezielle Methoden und Operationen zur Verfügung stellen, um die enthaltenen Daten möglichst effektiv verwalten und manipulieren zu können. Außerdem ist das Abfragen von Statusdaten des Arrays nun ebenfalls möglich.

Auf Grund dieser Vorteile sind jedoch auch einige Besonderheiten strikt einzuhalten. Diese betreffen vor allem das Anlegen eines Arrays auf dem Speicher und den Wunsch, ein Array zu kopieren. Mehr dazu in den folgenden Abschnitten.


» Deklaration eines Arrays nach oben «

Bei der Deklaration eines Arrays wird der eigentliche Container erzeugt. Das bedeutet, dass das Array zunächst exakt in seiner Größe und Typ bestimmt wird.

Möglich ist die Verwendung aller elementaren und benutzerdefinierten Datentypen. Die Deklaration wird mit dem Datentyp eingeleitet, gefolgt vom Namen des Arrays. Wichtig ist die Kennzeichnung des Arrays mit den eckigen Klammeroperatoren, welche eine Array - Variable ausweisen. Dabei spielt es keine Rolle, ob diese nach dem Typ oder dem Namen erscheinen. Zum Abschluß wird das Array noch mit dem New - Operator auf dem Speicher instanziiert, da es sich um eine Objektinstanz handelt. Auch wenn diese nicht sofort erfolgen muss, sollte sie dennoch spätestens im Konstruktor der Klasse instanziiert werden.


Typ[] Name = new Typ[Anzahl];
Typ Name[] = new Typ[Anzahl];
    

Das folgende Beispiel verwendet beide Methoden zur Deklaration eines Arrays.


public class MyClass
{
  public static void main(String[] args)
  {
    int[] array1 = new int[10];
    int array2[] = new int[20];    
  }
}
    

Im Beispiel sehen wir die Deklaration zweier Arrays, wobei einmal zehn und einmal zwanzig Integer allokiert werden. Wie bereits erwähnt, so kann die Position der eckigen Klammern selbst gewählt werden.

Des weiteren können auch benutzerdefinierte Datentypen in Form von Klassendefinitionen als Datentyp in Arrays verwendet werden. Das folgende Beispiel definiert eine Klasse samt Datenelement und speichert zwei dieser Klassen in einem dynamischen Array. Als Datentyp fungiert hierbei der Klassenname.


class MyClassType
{
  public int i;
}
      
public class MyClass
{
  public static void main(String[] args)
  {
    MyClassType[] array = new MyClassType[2];
  }
}
    

» Zugriff auf Elemente nach oben «

An dieser Stelle wollen wir Ihnen kurz vermitteln, wie es möglich ist, auf die Elemente eines Array zuzugreifen. Dabei gibt es einige wichtige Dinge zu beachten.

Zunächst einmal wird über einen numerischen Index auf alle Elemente zugegriffen, welcher in eckigen Klammern dem Namen des Array folgt. Allerdings wird das erste Element des Arrays stets mit null referenziert und erst das Folgelement mit eins. Dazu folgende Skizze.

Indexierung von Elementen eines Arrays

Beim Zugriff auf die Elemente hat man also immer einen Zahlenwert weniger als Elemente bei der Deklaration vereinbart worden sind. Das folgende Beispiel verdeutlicht das.


Name[Index];
    

Das folgende Beispiel legt ein Array mit zwei Elementen an, wonach das erste Element ausgegeben wird.


public class MyClass
{
  public static void main(String[] args)
  {
    int[] array = new int[2];
    System.out.print(array[0]);    
  }
}
    

0
    

Eventuell wird erfahrenen Programmieren aus anderen Programmiersprachen auffallen, dass wir in diesem Beispiel den Inhalt der ersten Array - Variable ausgegeben haben, obwohl dieser noch kein Wert zugewiesen wurde. In Java spielt dies keine Rolle, da alle Elemente mit einem festen Wert vorbelegt sind. In diesem Fall mit null.

Vorhin haben wir gesehen, dass auch benutzerdefinierte Klassen verwendet werden können. Wie diese speziell in Arrays verwaltet werden und wie es möglich ist, über das Array auf geschachtelte Klassenelemente zuzugreifen, wollen wir aber auf Kapitel zehn verschieben, wo Klassen im Detail behandelt werden.


» Initialisierung nach oben «

Mit den soeben erworbenen Kenntnissen im Umgang mit Arrayelementen ist es uns nun gestattet, wieder einen Schritt weiter zu gehen und die Initialisierung von Array zu besprechen.

Prinzipiell handelt es sich um einen ganz simplen Vorgang. Jedem Element eines Array kann einfach bei dessen Referenzierung per Zuweisungsoperator ein passender Wert zugeordnet werden. Dabei ist allerdings der Datentyp wichtig, denn wie wir wissen, führt Java nur eine bedingte Konvertierung von Datentypen durch.


Name[Index] = Wert;
    

Das folgende Beispiel weist einem Array nachträglich zwei Werte zu.


public class MyClass
{
  public static void main(String[] args)
  {
    int[] array = new int[2];
    
    array[0] = 10;
    array[1] = 20;    
  }
}
    

Bei diesem Beispiel handelt es sich um ein relativ kleines Array mit gerade einmal zwei Elementen. In der praktischen Programmierung sind allerdings Arrays mit dutzenden oder gar hunderten von Elementen keine Seltenheit.

Hierbei bedient man sich dem Konzept der Schleifen. Auch wenn diese erst im Kapitel sieben ausführlich behandelt werden, so möchten wir deren Nützlichkeit im Umgang mit Arrays dennoch demonstrieren. Auch wenn Sie die folgenden Codezeilen nicht verstehen sollten, so spielt dies vorerst keine Rolle, später wird alles klarer. Programmierer in anderen Sprachen als Java werden hier aber bereits Parallelen erkennen.


public class MyClass
{
  public static void main(String[] args)
  {
    int[] array = new int[100];
		
    for(int i=0; i<100; i++)
      array[i] = i;    
  }
}
    

» mehrdimensionale Arrays nach oben «

Java unterstützt das Konzept mehrdimensionaler Arrays. Dabei handelt es sich um Arrays mit zwei oder mehr Dimensionen. Zieht man sich die Implementierungsdetails heran, so kann man mehrdimensionale Arrays als eine Art Verschachtelung derselben ansehen.

Dimensionen eines mehrdimensionalen Arrays

Dabei können beliebig viele Dimensionen erzeugt werden. Grundlegend werden die Dimensionen eine nach der anderen aufgeschlüsselt, bis das letzte Array erreicht ist. Diese letzte Dimension beinhaltet dann die Elemente. An sich funktionieren mehrdimensionale Arrays äquivalent zu Ihren eindimensionalen Versionen, dennoch möchten wir hier kurz die wesentlichen Schritte zur Erzeugung und Nutzung nachvollziehen.


» Speichermanagement

Mehrdimensionale Arrays werden ebenfalls in einer Reihe im Speicher angeordnet. Lediglich die Reihenfolge der verschiedenen Subarrays ist zu beachten. Bei der Anlegung der im Speicher befindlichen Arrays wird stets mit dem Array begonnen, welches als erstes die letzte Dimension bildet, sprich das erste eigentliche Array mit Elementen. Erst daraufhin folgen sequentiell alle weiteren Felder, bis zum letzten Array.

Die Speichergröße des gesamten Arrays ergibt sich aus der Multiplikation aller Indexe aller Dimensionen, sowie der Bytegröße des zugrundeliegenden Datentyps.

» Deklaration eines Arrays

Die Deklaration unterscheidet sich nur geringfügig von der bisher bekannten Methode. Es müssen lediglich soviele eckige Klammern angegeben werden, wie Dimensionen erstellt werden sollen. Die erste Klammer bildet dabei die erste und die letzte Klammer entsprechend die letzte Dimension.


Typ[][] Name = new Typ[Anzahl][Anzahl];
Typ Name[][] = new Typ[Anzahl][Anzahl];
    

Folglich deklarieren wir zwei Arrays. Die erste Dimension hat zehn und die zweite Dimension zwanzig Elemente. Insgesamt lassen sich also 200 Elemente verwalten.


public class MyClass
{
  public static void main(String[] args)
  {
    int[][] array1 = new int[10][20];
    int array2[][] = new int[10][20];    
  }
}
    

» Zugriff auf Elemente

Der Zugriff auf die Elemente unterliegt den gleichen Regeln wie die eindimensionalen Arrays. Der Programmierer muss allerdings die exakte Position des Elements durch die Angabe jeder Dimension exakt aufschlüsseln. Begonnen wird wiederum bei der ersten Dimension.


Name[Index][Index];
    

Wir deklarieren ein zweidimensionales Array und geben danach das zweite Element der ersten Dimension aus.


public class MyClass
{
  public static void main(String[] args)
  {
    int[][] array = new int[2][2];
    System.out.print(array[0][1]);    
  }
}
    

0
    

» Initialisierung

Wie gewohnt kann jedes einzelne Element über seinen Index referenziert werden. Die Angabe aller Dimensionen ist erforderlich.


Name[Index][Index] = Wert;
    

Wir deklarieren ein zweidimensionales Array mit zwei mal zwei Elementen, dessen Elemente wir danach mit einem Wert initialisieren.


public class MyClass
{
  public static void main(String[] args)
  {
    int[][] array = new int[2][2];
    
    array[0][0] = 10;
    array[0][1] = 20;
    array[1][0] = 30;
    array[1][1] = 40;    
  }
}
    

Mit etwas mehr Aufwand können wir mit Hilfe der Schleifen auch große mehrdimensionale Arrays initialisieren lassen. Dieses Thema wird, wie zuvor besprochen, noch einmal später genauer aufgegriffen.


public class MyClass
{
  public static void main(String[] args)
  {
    int[][] array = new int[10][20];
		
    for(int i=0; i<10; i++)
      for(int j=0; j<20; j++)				
        array[i][j] = i+j;    
  }
}
    

» Kopieren von Arrays nach oben «

Deklariert man in Java ein Array, so handelt es sich auf Grund des Speichermanagements lediglich um eine Referenz auf den Speicherbereich, sprich eine Variable welche die Speicheradresse aufnimmt.

Was aber passiert, wenn wir das Array kopieren wollen? In diesem Fall kopiert man nur die Variable, welche die Adresse enthält und sobald man auf die Elemente der Kopie zugreift, bezieht man sich automatisch auf das Originalarray.


public class MyClass
{
  public static void main(String[] args)
  {
    int[] array = new int[2];
    int[]  copy = array;
		
    array[0] = 0;
    array[1] = 1;
		
    System.out.println(array[0]);
    System.out.println(array[1]);
		
    copy[1] = 2;
		
    System.out.println(array[1]);    
  }
}
    

0
1
2
    

Die folgende Abbildung soll verdeutlichen, dass die Zieladresse der Arrayvariable kopiert wurde und somit ein und das selbe Array referenziert wird. Dies wird auch als flache Kopie bezeichnet.

Prinzip einer flachen Kopie

Eine tiefe Kopie allerdings kopiert das gesamte Array in einen neuen und vom Original unabhängigen Speicherbereich, wo dann alle Elemente des Arrays noch einmal vorliegen. Dazu bedient man sich einer in Java bereits fertig programmierten Methode, welche wie folgt definiert ist.


Die Methode befindet sich im System - Package von Java und wird dort global zur Verfügung gestellt. Im folgenden Beispiel werden wir diese Methode nutzen, um eine tiefe Kopie des Arrays zu erzeugen, wonach das Speicherabbild wie folgt dargestellt wird.

Prinzip einer tiefen Kopie


public class MyClass
{
  public static void main(String[] args)
  {
    int[] array = {0,1,2,3,4};
    int[]  copy = {0,0,0,0,0};
		
    System.arraycopy(array,1,copy,1,3);
		
    for(int i=0; i<copy.length; i++) 
      System.out.println(copy[i]);
  }
}
    

0
1
2
3
0
    

Das Beispiel kopiert drei Elemente. Dabei wird im Quellarray ab dem zweiten Index begonnen. Alle drei kopierten Variablen werden in das Zielarray ab der zweiten Indexposition eingefügt.


» Arrays als Parameter nach oben «

Unser vorletzter Abschnitt soll sich mit dem Thema der Funktionsparameter in Form von Arrays auseinandersetzen. Dabei werden wir etwas vorgreifen, wobei Sie auch hier das Dargestellte vorerst so akzeptieren sollten, ohne sich einen Kopf über den genauen Hintergrund machen zu müssen.

Bereits erfahrenen Prorammierern allerdings wird hier schon einiges klarer. Im Kapitel der Klassen wird dieser Teil noch ausführlich behandelt werden.

Viele der Methoden von Java und auch benutzerdefinierte Funktionen können Arrays als Parameter übernehmen, um mit diesen zu arbeiten. Eine derartige Methode muss den entsprechenden Parameter nur mit dem Typ des Arrays deklarieren. Das Array selbst wird beim Aufruf der Methode lediglich durch seinen Namen übergeben.


[Modifikator] Typ Methode(Typ[] Parameter)
{
  // Anweisungen
}
    

Unser nächstes Beispiel erzeugt eine statische Methode, die ein Integerarray übernimmt und alle seine Elemente ausgibt.


public class MyClass
{
  public static void print(int[] a)
  {
    for(int i=0;i<a.length;i++)
      System.out.print(a[i]);
  }
	
  public static void main(String[] args)
  {
    int[] array = new int[10];
		
    for(int i=0;i<array.length;i++)
      array[i] = i;
		
    print(array);
  }
}
    

0123456789
    

Eine letzte Anmerkung zum Thema soll noch einmal darauf hinweisen, dass ein deklariertes Array stets nur die eigentliche Adresse der Variablen speichert und somit eine Referenz darstellt. Übergibt man also das Array an eine Funktion, so wird nur die Adresse kopiert und alle Änderungen am Parameter beziehen sich auch auf das Original.


» Rückgabe von Arrays nach oben «

Abschließend betrachten wir den Fall, dass ein Array aus einer Methode heraus zurückgegeben wird. Auch dieses Thema greifen wir detailliert in den entsprechenden Kapiteln noch einmal auf.

Um ein Array liefern zu können, muss die entsprechende Methode natürlich entsprechend deklariert sein. Dabei wird die Angabe des Rückgabetyps auf den Arraytyp abgestimmt. Innerhalb der Methode erfolgt die Rückgabe einfach durch den Namen des Arrays in Verbindung mit dem Return - Befehl.


return Array;
    

Das folgende Beispiel nutzt zusätzlich eine Methode, welche ein Array erzeugt, mit Werten füllt und dann an das Programm zurückgibt.


public class MyClass
{
  public static void print(int[] a)
  {
    for(int i=0;i<a.length;i++)
      System.out.print(a[i]);
  }
  
  public static int[] init()
  {
    int[] tmp = new int[10];
		
    for(int i=0;i<tmp.length;i++)
      tmp[i] = i;
		
    return tmp;
  }
	
  public static void main(String[] args)
  {
    int[] array = init();		
    print(array);
  }
}
    

0123456789
    

« Kapitel Kapitelübersicht nach oben Kapitel »