Axel Rogat
Objektorientiertes Programmieren mit C++ und JAVA
 
21.9: Referenz-Typen Kapitel 21 22.1: Strings 
 
  21.10 Arrays  
 

In JAVA werden Arrays als spezielle Objekte aufgefaßt. Es handelt sich um einen Container, in dem andere Objekte geordnet gespeichert werden können. Seine Länge liegt nach der Definition fest. Es gibt Container mit variabler Länge (dazu später). Vorsicht: Es gibt eine JAVA-Klasse Container, die eine völlig andere Bedeutung hat als die Bezeichnung Container in C++!

Da Arrays selbst Objekte sind, können alle Operationen auf sie angewandt werden, die mit Objekten möglich sind. Beispielsweise werden sie garbage-collected, man kann sie in Container einfügen, serialisieren, etc.

Der Typ "Array über dem Grundtyp T" ist in JAVA ein echter neuer Typ T[]. Wenn das erste Mal ein Array über T definiert wird, wird dieser Typ intern automatisch kreiert, und auf das Array wird danach über Referenzen auf T[] zugegriffen.

Man kann mit instanceof auch die Zugehörigkeit eines Arrays zu einem solchen Array-Typen testen:

int arr[][]=new int[3][4]; if (arr instanceof int[][]) ...
Die Definition und der Element-Zugriff erfolgt wie in C++ mit den eckigen Klammern []. Diese werden aber nicht mehr als Operator aufgefaßt und sind auch nicht mehr überladbar. Entsprechende eigene Mechanismen müßte man dann mit Funktionsaufrufen wie read und write realisieren. Die Klammern sind auch nicht mehr kommutativ, so daß man nicht 3[array] für array[3] schreiben kann.

Die Definition einer Array-Referenz kann in zwei Formen erfolgen, eine aus C++ (aus Gewohnheitsgründen) übernommen, eine weitere, die den gesamten Array-Typnamen voranstellt:

int[] int_array1; int int_array2[];
Wie bei allen Referenz-Typen ist hier noch kein (Array-)Objekt angelegt worden, und deshalb wird hier die Länge des Arrays noch nicht spezifiziert. Das geschieht erst beim new-Aufruf:
int_array1=new int[mylen+3];
Vorsicht: Mit der Deklaration
int[] array3, array4[];
wird array4 ein zweidimensionales Array äquivalent zu int[][] array4;

Die Indizierung beginnt wie in C++ immer bei 0.

Bei Arrays über einfachen Typen haben die einzelnen Elemente nach dem new-Aufruf den Wert 0 bzw. false. Eine Initialisierung mit einer {}-Liste ist genau wie in C++ möglich:

int[] fib={ 1,2,3,5,8,13,21,34,55,89,144,233 };
Bei Arrays über Referenz-Typen sind alle Referenzen zunächst uninitialisiert, d.h. null! Jedes einzelne echte Element muß nach der Definition der Array-Referenz explizit mit dem new-Operator angelegt werden. Danach kann man sich die Referenz-Struktur vorstellen wie unten rechts dargestellt.
Double[] d_arr=new Double[4]; d_arr[0]=new Double(3.1415926); d_arr[1]=new Double(2.7182818); d_arr[3]=new Double(1.4142135);

Es ist aber auch eine Listen-Schreibweise möglich wie folgt:

Double[] d_arr= { new Double(3.1415926), new Double(2.7182818), null, new Double(1.4142135) };
oder in alternativer Schreibweise:
Double[] d_arr=new Double[] { new Double(3.1415926), new Double(2.7182818), null, new Double(1.4142135) };

Auf diese Weise können auch anonyme Arrays angelegt werden, die nur an einer Stelle benötigt werden, zum Beispiel in einem Funktionsaufruf, und die gar keinen eigenen Namen zu bekommen brauchen:

public class test { public static void myfunc(Integer[] a) { for (int i=0;i<a.length;++i) System.out.println(a[i]); } public static void main(String[] args) { myfunc(new Integer[] { new Integer(1), new Integer(2) }); } }

Im Beispiel wird int length verwendet, die Anzahl der Elemente des Arrays. Dabei handelt es sich um den einzigen öffentlichen Daten-Member jedes Array-Typs.

Zugriffe mit negativen oder zu großen Indizes werfen eine Ausnahme mit dem Namen ArrayIndexOutOfBoundsException. Da das ein Untertyp von RuntimeException ist, braucht sie nicht abgefangen zu werden (dazu später).

Eine Zuweisung mit = kopiert wiederum nicht das Objekt (also hier das ganze Array), sondern nur die Referenz darauf:

Float[] arr1=new Float[32]; Float[] arr2=new Float[64]; arr1=arr2; // jetzt: 32er-Array unbenutzt, wird freigegeben!
Die Länge eines Arrays ist nicht Teil des Typs (hier: Float[], nicht Float[32]). Deshalb sind solche Referenz-Zuweisungen auch bei unterschiedlichen Längen möglich.

Für das Kopieren eines ganzen Arrays gibt es eine spezielle Routine System.arraycopy (das Ziel-Array muß dafür aber schon angelegt sein):

public static native void arraycopy ( Object src, int src_position, Object dst, int dst_position, int length );
Dabei können Start-Index im Quell- und im Ziel-Array sowie die Anzahl der zu kopierenden Elemente angegeben werden.

Mehrdimensionale Arrays sind mit mehreren []-Paaren wie in C++ möglich. Es werden intern entsprechend viele Zwischentypen erzeugt:

String[][][] muldim = new String[2][2][2]; muldim[0][0][0]="0"; muldim[1][1][1]="7";
Hier werden die Typen String[], String[][] und String[][][] kreiert.

Wenn man Dimensionen in der Definition wegläßt, können sie automatisch durch passende Strukturierung der Initialisierungsliste festgelegt werden:

String[][] info= { { "NCC-1701", "Constitution", "289m" }, { "NCC-1701-A", "Constitution", "305m" }, { "NCC-1701-B", "Excelsior", "467m" }, { "NCC-1701-C", "Ambassador", "526m" }, { "NCC-1701-D", "Galaxy", "641m" }, { "NCC-1701-E", "Sovereign", "????" }, };
Die beiden Dimensionen können danach mit info.length bzw. info[0].length bestimmt werden (statt 0 darf natürlich jeder vorn erlaubte Index stehen, also 0 bis 5).

Die Dimensionen der Unter-Arrays brauchen aber nicht einmal untereinander gleich zu sein. Beispielsweise erhält man wie folgt eine untere 4×4-Dreiecksmatrix, ohne daß dabei Speicher verschwendet wird:

int[][] lower={ {1} , {0,1} , {0,0,1} , {0,0,0,1} };
Es braucht bei mehrdimensionalen Arrays (ohne Initialisierung) immer nur die vorderste Dimension angegeben zu werden. Die Unterarrays bleiben dann selbst uninitialisiert, und ihre Größen können nachgetragen werden. Dadurch kann man eine Dreiecksmatrix auch mit variabler Größe definieren:

int n=10; double[][] lower=new double[n][]; for (int i=0;i<n;++i) { lower[i]=new double[i+1]; for (int j=0;j<=i;++j) lower[i][j]=i+j; }

Dadurch, daß in JAVA alle Klassen von der Klasse Object erben, immer Laufzeit-Typinformationen mitgeführt werden und alle Funktionen virtuell sind, kann man sogar Arrays mit gemischten Typen verwenden:

Object[] mix=new Object[3]; mix[0]=new Integer(42); mix[1]=new String("Sm\u00f8rebr\u00f8d"); mix[2]=new Vector(); ((Vector)mix[2]).addElement(new Double(1.5)); System.out.println(mix[0]); // Ausgabe "42" System.out.println(mix[1]); // Ausgabe "Smørebrød" System.out.println(mix[2]); // Ausgabe "[1.5]"

Das ist der Grund dafür, warum der Ausfall von Templates in JAVA nicht so schwerwiegt, wie es zunächst erscheinen mag. Auf ähnliche Weise sind die Standard-Container wie Vector, Stack, etc. implementiert.

 
21.9: Referenz-Typen Startseite 22.1: Strings 
 

© 1998 Axel Rogat