3.4 Arrays
 
Ein Array (auch »Feld« oder »Reihung« genannt) ist ein spezieller Datentyp, der mehrere Werte, die über einen ganzzahligen Index angesprochen werden, zu einer Einheit zusammenfasst. Er ist vergleichbar mit einem Setzkasten, in dem die Plätze durchnummeriert sind. Jeder Platz (etwa für Schlümpfe) nimmt immer Werte des gleichen Typs auf (nur Schlümpfe und keine Pokemons). Normalerweise liegen die Plätze eines Arrays (seine Elemente) im Speicher hintereinander, doch dies ist ein Implementierungsdetail, welches in Java unwichtig ist.
Jedes Array beinhaltet Werte eines bestimmten Datentyps. Dies können sein:
|
Elementare Datentypen wie int, byte, long und so weiter |
|
Referenzen auf Objekte |
|
Referenzen auf andere Arrays, um mehrdimensionale Arrays zu realisieren |
3.4.1 Deklaration von Arrays
 
Eine Array-Variablendeklaration ähnelt einer gewöhnlichen Deklaration, nur dass nach dem Datentyp oder den Variablen die Zeichen »[« und »]« gesetzt werden müssen. Uns ist es freigestellt, welche Schreibweise wir wählen. Hauptsache, es werden überhaupt Klammern gesetzt. Wie bei der gesamten Programmierung, so sollte auch hier konsistent vorgegangen werden – einmal so, einmal so, behindert die schnelle Wahrnehmung von Programmquelltext.
int schachfeld[];
int [] auchSchach;
Point punkte[];
Die Klammern [ ] vor oder hinter die Variable setzen?
So ganz ohne Unterschied ist die Deklaration nicht. Das zeigt sich spätestens, wenn mehr als eine Variable deklariert wird. Die Klammern können einerseits Teil des Typs sein, andererseits Teil der Variablen. Sind sie Teil des Typs, so sind alle deklarierten Variablen ein Feld. Es entspricht demnach
int[] prims, matrix[], 3dmatrix[][];
der Deklaration
int prims[], matrix[][], 3dmatrix[][][];
Hier ist doppelt Vorsicht geboten, denn der eine oder andere wollte vielleicht nur
int []prims, i;
schreiben, um auszudrücken, dass i eine normale Ganzzahlvariable ist. Stattdessen würde der Compiler jedoch annehmen, dass i ein Feld ist und eine Zuweisung der Art i=2 gnadenlos ablehnen. Es ist aber nicht direkt ersichtlich, wo der Fehler liegt. Ich empfehle daher, die Klammern hinter den Bezeichner und bestenfalls in jede Zeile nur eine Deklaration zu setzen. (Aus diesem Grund ist es auch ungünstig, unter C(++) das Sternchen für den Zeiger direkt an den Datentyp zu packen.1
) Das beugt Fehlern vor. Nach reiner Java-Lehre jedenfalls gehören die Klammern hinter den Typbezeichner, so hat es Gosling gewollt.
3.4.2 Arrays mit Inhalt
 
Die bisherigen Deklarationen von Array-Variablen erzeugen noch lange kein Array-Objekt, das die einzelnen Array-Elemente aufnehmen kann. Wenn allerdings die Einträge direkt mit Werten belegt werden sollen, so gibt es in Java eine Abkürzung, die ein Array-Objekt anlegt und zugleich mit Werten belegt.
Beispiel Wertebelegung eines Felds
int primiMäuschen[] = { 1, 2, 3, 5, 7, 7+4, };
String substantive[] = {
|
"Haus",
"Maus",
translator.toGerman( "dog" ), // fiktives Objekt translator
new Point().toString()
};
|
In diesem Fall wird ein Feld mit passender Größe angelegt und die Elemente werden in das Feld kopiert, die in der Aufzählung genannt sind. Innerhalb der Aufzählung kann als Letztes ein Komma stehen, wie die Aufzählung bei primiMäuschen demonstriert.
Es ist nicht möglich – wie in C(++) – das Feld mit einer bestimmten Größe zu initialisieren, ohne gleichzeitig die Werte aller Elemente aufzuzählen.
Beispiel Folgende Zeile ist in Java falsch und führt zu einem Compilerfehler:
int einhundertElemente[100]; // Compilerfehler
|
Wir müssen diesen Fall über eine explizite Objekterzeugung lösen, wie wir etwas später mit einem new-Operator sehen werden.
Strings und keine Arrays
Ein Array von Char-Zeichen ist nicht mit einem String vergleichbar. Die Klasse String bietet jedoch einen Konstruktor an, sodass aus einem Feld mit Zeichen ein String-Objekt erzeugt werden kann. Alle Zeichen des Felds werden kopiert, sodass anschließend Feld und String keine Verbindung mehr besitzen. Das bedeutet, falls sich das Feld ändert, ändert sich der String nicht automatisch mit. Das kann er auch nicht, da Strings unveränderlich sind.
Beispiel Mit der Methode toCharArray() können wir einen String in ein char-Feld konvertieren.
char umlaut[] = "aeiouäöü".toCharArray();
|
3.4.3 Die Länge eines Arrays mit length
 
Die Anzahl der Elemente, folglich die Länge des Arrays, ist für jedes Array-Objekt in der frei zugänglichen Objektvariablen length gespeichert. length ist eine public final int Variable, deren Wert entweder positiv oder Null ist.
Beispiel Ein Feld und Ausgabe der Länge
int primiMäuschen[] = { 1, 2, 3, 5, 7, 7+4, };
System.out.println( primiMäuschen.length );
|
Feldlängen sind final
Das Attribut length eines Felds ist nicht nur öffentlich (public) und vom Typ int, sondern natürlich auch final. Schreibzugriffe sind nicht gestattet. (Was sollten sie bewirken? Eine dynamische Vergrößerung des Felds?) Ein Schreibzugriff führt zu einem Übersetzungsfehler.
3.4.4 Zugriff auf die Elemente
 
Die Anzahl der Elemente, die ein Array aufnehmen kann, wird auch als Größe beziehungsweise Länge bezeichnet. In Java beginnt ein Array, ähnlich wie in C(++), bei 0 (und nicht bei einer frei wählbaren Untergrenze wie in PASCAL). Die Größe lässt sich später nicht mehr ändern. Da die Elemente eines Arrays ab 0 nummeriert werden, ist der letzte gültige Index um 1 kleiner als die Länge des Felds. Der Zugriff auf die Elemente eines Felds erfolgt mit Hilfe der eckigen Klammern [], die hinter die Referenz an das Array-Objekt gesetzt werden. Bei einem Array a der Länge n ist der gültige Bereich somit a[0] bis a[n-1].
Beispiel Greife auf das erste und letzte Zeichen aus dem Feld zu.
char name[] = "ABC".toCharArray[];
char first = name[0];
char last = name[name.length-1];
|
Über den Typ des Index
Innerhalb der eckigen Klammern steht ein Ganzzahl-Ausdruck, der sich zur Laufzeit berechnen lassen muss. long-Werte sowie Gleitkommazahlen sind nicht möglich. Bei Long-Werten wäre der Wertebereich zu groß, denn ein int-Index erlaubt ja schon mehr als 2,4 Milliarden Elemente. Bei Gleitkommazahlen bliebe die Frage nach der Zugriffstechnik. Hier müssten wir den Wert auf ein Intervall herunterrechnen.
Beispiel Liegt etwa eine Fließkommazahl f im Intervall von 0 bis 1 und haben die Werte eine Genauigkeit von einem Tausendstel, so ließe sich für ein Array a mit 1000 Elementen für eine Konsolenausgabe schreiben:
f = 0.01; // im Intervall von 0 bis 1
System.out.println( a[(int)(f*1000 )] )
|
Index vom Typ char ist auch ein int
Der Index eines Felds muss von einem Typ sein, der ohne Verlust auf int konvertierbar ist. Dazu gehören byte, short und char.
Beispiel Das Zeichen c soll in eine Java-Unicode-Zeichenfolge der Form \uxxxx umgewandelt werden.
public static String charToUnicodeEscape( char c )
{
char chars[] = { '\\', 'u',
|
hexchars[c >> 12 & 0xf], hexchars[c >> 8 & 0xf],
hexchars[c >> 4 & 0xf], hexchars[c & 0xf] };
return new String( chars );
}
private static final char hexchars[] =
"0123456789".toCharArray();
};
|
Genau genommen haben wir es auch hier mit Indexwerten vom Typ int zu tun, weil mit den char-Werten vorher noch gerechnet wird.
Obwohl es Hexzeichen-Felder schon in anderen Klassen gibt (etwa in Properties), sind diese oft privat. Wenn wir ein eigenes Array in der Klasse definieren, hat das zusätzlich den Vorteil, dass keine eventuell unerwünschten Abhängigkeiten zu anderen Klassen entstehen.
3.4.5 Array-Objekte erzeugen
 
Ein Array muss mit dem new-Operator unter Angabe einer festen Größe erzeugt werden. Das Anlegen der Variablen alleine erzeugt noch kein Feld mit einer bestimmten Länge. In Java ist das Anlegen des Felds genauso dynamisch wie die Objekterzeugung. Dies drückt auch der new-Operator aus. Die Länge des Felds wird in eckigen Klammern angegeben. Hier kann ein beliebiger Integer-Wert stehen, auch eine Variable.
Beispiel Die zweite Zeile erzeugt ein Array-Objekt für 100 Elemente.
int arrayOfInts[];
arrayOfInts = new int[100];
Die Felder mit den primitiven Werten sind mit 0, 0.0 oder false initialisiert.
|
Beispiel Deklaration eines 10-elementigen Felds und Initialisierung der Elemente.
double x[] = new double[10]; // dann gilt für die Indexwerte 0 <= x <= 9
for ( int i = 0; i < 10; i++ )
x[i] = 2*i;
|
Beispiel Günstig ist ein Index vom Typ char, der automatisch zum int konvertiert wird, zum Beispiel als Laufvariable, wenn Felder von Zeichenketten generiert werden.
char alphabet[] = new char['z'-'a'+1];
for( char c = 'a'; c <= 'z'; c++ )
alphabet[c-'a'] = c;
|
Das ist auch eine elegante und schnelle Möglichkeit, Strings zu erzeugen, denn die Zeichenkette kann mit einem String(char[])-Konstruktor in ein String-Objekt umgewandelt werden:
String result = new String( alphabet );
3.4.6 Fehler bei Arrays
 
Beim Zugriff auf ein Array-Element können Fehler auftreten. Zunächst einmal kann das Array-Objekt fehlen, sodass die Referenzierung fehlschlägt. Etwa in dem folgenden Fall, bei dem der Compiler den Fehler nicht bemerkt:2
int feld[];
feld[1] = 1;
Die Strafe ist eine NullPointerException.
Der zweite und dritte Fehler liegt im Index begründet. Dieser könnte negativ sein oder über der maximalen Länge liegen. Jeder Zugriff auf das Feld wird zur Laufzeit getestet. Auch bei Operationen, die für den Compiler entscheidbar wären, wird dieser Weg eingeschlagen, etwa bei den nachfolgenden Zeilen:
int feld[] = new int[100];
feld[100] = 100;
Hier könnte der Compiler theoretisch Alarm schlagen, was aber kaum ein Compiler bisher macht, denn der Zugriff auf Elemente mit einem ungültigen Index ist syntaktisch und statisch semantisch völlig in Ordnung.
Ist der Index negativ3
oder zu groß, dann hagelt es eine IndexOutOfBoundException. Wird diese nicht abgefangen, bricht das Laufzeitsystem das Programm mit einer Fehlermeldung ab.
Index und das Inkrement
Wir haben beim Inkrement schon ein Phänomen wie i=i++ betrachtet. Ebenso ist auch die Anweisung bei einem Feldzugriff zu behandeln.
a[i] = i++;
Bei der Position a[i] wird i gesichert und anschließend die Zuweisung gemacht. Wenn wir darum eine Schleife konstruieren, erweitern wir dies zu einer Initialisierung:
int is[] = new int[4];
int i = 0;
while ( i < is.length )
is[i] = i++;
Die Ausgabe ergibt 0, 1, 2 und 3.
3.4.7 Arrays mit nichtprimitiven Elementen
 
Der Datentyp der Array-Elemente muss nicht zwingend ein primitiver sein. Auch ein Array von Objektreferenzen kann deklariert werden. Dieses Array besteht dann nur aus Referenzen auf die eigentlichen Objekte, die in dem Array abgelegt werden sollen. Die Größe des Arrays im Speicher errechnet sich demnach aus der Länge des Felds multipliziert mit dem Speicherbedarf einer Referenz. Nur das Array-Objekt selbst wird angelegt, nicht aber die Objekte, die das Array aufnehmen soll. Dies lässt sich einfach damit begründen, dass der Compiler auch gar nicht wüsste, welchen Konstruktor er aufrufen sollte.
Beispiel Ein nichtprimitives Feld mit fünf Punkt-Objekten
Point punkte[] = new Point[5];
Hier wird Platz für 5 Referenzen auf Punkte gemacht, aber kein einziges Point-Objekt angelegt. Später würde das Feld etwa mit point[0] = new Point() gefüllt. Standardmäßig werden die Array-Elemente mit der Null-Referenz initialisiert.
|
Beispiel Fünf Punkte werden angelegt und mit willkürlichen Werten gefüllt. Die Zufallszahlen werden dabei mit Hilfe der mathematischen Funktion Math.random() erzeugt.
Point punkte[] = new Point[5];
for ( int i = 0; i < punkte.length; i++ )
punkte[i] = new Point( (int)(Math.random()*100), (int)(Math.random()*100) );
for ( int i = 0; i < punkte.length; i++)
System.out.println( punkte[i] );
Die Ausgabe erzeugt zum Beispiel Folgendes:
java.awt.Point[x=59,y=77]
java.awt.Point[x=47,y=86]
java.awt.Point[x=18,y=71]
java.awt.Point[x=55,y=97]
java.awt.Point[x=12,y=70]
|
3.4.8 Arrays und Objekte
 
Wir haben gesehen, dass Arrays Objekte sind, die geordnete Elemente enthalten. Trotz der weitgehenden Übereinstimmung mit gewöhnlichen Objekten, sollten wir die Unterschiede nicht verschweigen:
|
Mit dem Operator [] kann auf Array-Elemente über ihren ganzzahligen Index zugegriffen werden. Dieser Operator wird bei anderen Objekten nicht angeboten. |
|
Eine spezielle Form des new-Operators erzeugt ein Exemplar der Array-Klasse. |
|
Eine entsprechende Array-Klasse wird automatisch generiert, wenn ein Array-Typ deklariert wird. |
3.4.9 Initialisierte Array-Objekte
 
Wenn wir in Java ein Array-Objekt erzeugen und gleich mit Werten initialisieren wollen, dann schreiben wir etwa:
int primi[] = { 2, 5, 7, 11, 13 };
Wollen wir uns erst nach der Variablendeklaration für die Feldinhalte interessieren und sie gegebenenfalls auch ändern, schlägt ein Versuch wie der folgende fehl:
int primi[];
primi = { 2, 5, 7, 11, 13 }; // illegal start of expression
Besonders ärgerlich wird dies bei der Parameterübergabe. So scheitert der folgende praktische Funktionsaufruf:
ausgleichsgerade( { 1.23, 4.94, 9.33, 3.91, 6.34 } );
Das Einzige, was spontan zur Lösung dieses Problems beiträgt, ist die Einführung einer neuen Variablen:
int primi[];
int tmpprimi[] = { 2, 5, 7, 11, 13 };
primi = tmpprimi;
Ein Feld ohne Namen
Glücklicherweise gibt eine Variante des new-Operators, der durch ein Paar eckiger Klammern erweitert wird. Es folgen in geschweiften Klammen die Initialwerte des Arrays. Die Größe des Arrays entspricht genau der Anzahl der Werte.
Für die oberen Beispiele ergibt sich folgende Schreibweise:
int primi[];
primi = new int[]{ 2, 5, 7, 11, 13 };
ausgleichsgerade( new double[]{ 1.23, 4.94, 9.33, 3.91, 6.34 } );
Da, wie im zweiten Beispiel, ein initialisiertes Feld mit Werten gleich an die Funktion übergeben wird und keine zusätzliche Variable benutzt wird, wird diese Art der Arrays »anonyme Arrays« genannt. Eigentlich gibt es auch sonst anonyme Arrays, wie new int[2000].length zeigt. Doch in diesem Fall wird das Feld nicht mit Werten initialisiert.
Möglicherweise wird sich die Schreibweise bei Java 1.5 vereinfachen. So schlägt der Java Specification Requests 65 »Concise Object-Array Literals« vor, dass direkt ein Paar geschweifter Klammern ein Feld von Objekten bildet.
Variable Parameterlisten
Mit dem Konzept der initialisierten Array-Objekte lässt sich eine variable Parameterliste nachstellen. Wir schreiben einfach eine Methode, die ein Feld verlangt, und übergeben dann beim Aufruf ein entsprechend vorbelegtes Array-Objekt.
Listing 3.4
VariableParameter.java
public class VariableParameter
{
static double sum( double values[] )
{
double sum = 0;
for ( int i = 0; i < values.length; i++ )
sum += values[i];
return sum;
}
public static void main( String args[] )
{
System.out.println( sum(new double[]{ 1.5, 2.5 }) );
}
}
3.4.10 Mehrdimensionale Arrays
 
Java realisiert mehrdimensionale Arrays durch Arrays von Arrays. Sie können etwa für die Darstellung von mathematischen Matrizen oder Rasterbildern Verwendung finden. Ein zweidimensionales Feld mit dem Platz für Reihen von 8 Elementen definiert sich einfach über folgende Zeile:
int A[][] = new int[4][8];
Zwei alternative Deklarationen sind:
int[][] A = new int[4][8]; // Der Typ von A ist zweidimensionales Array
// mit Elementtyp int
int[] A [] = new int[4][8];
Einzelne Elemente werden mit A[i][j] angesprochen.4
Der Zugriff erfolgt mit so vielen Klammerpaaren, wie die Dimension des Arrays angibt. Obwohl mehrdimensionale Arrays im Prinzip Arrays mit Arrays als Elementen sind, lassen sie sich leicht deklarieren.
Beispiel Der Aufbau von zweidimensionalen Feldern ist vergleichbar mit einer Matrix beziehungsweise Tabelle. Dann lässt sich der Eintrag im Feld a[x][y] in folgender Tabelle ablesen.
a[0][0] a[0][1] a[0][2] a[0][3] a[0][4] a[0][5] ...
a[1][0] a[1][1] a[1][2] a[1][3] a[1][4] a[1][5]
a[2][0] a[2][1] a[2][2] a[2][3] a[2][4] a[2][5]
...
|
Nicht rechteckige Felder
Da in Java mehrdimensionale Arrays als Arrays von Arrays implementiert sind, müssen diese nicht zwingend rechteckig sein. Jede Zeile im Feld kann eine eigene Größe haben.
Beispiel Ein dreieckiges Array mit Zeilen der Länge 1, 2 und 3.
int m[][] = new int[3][];
for ( int i = 0; i < 3; i++ )
m[i] = new int[i+1];
|
Der Vergleich von
int m[][] = new int [3][4];
int m[][] = new int [3][];
zeigt, dass im ersten Fall die passenden Unterfelder automatisch erzeugt werden. Dies ist im zweiten Fall nicht so. Hier müssen wir selbst die Unterfelder initialisieren, bevor wir auf die Elemente zugreifen.
Ebenso wie bei eindimensionalen Feldern lassen sich mehrdimensionale Felder gleich beim Anlegen initialisieren.
Beispiel Mehrdimensionale Felder initialisieren
int A3x2[][] = {{1,2}, {2,3}, {3,4}};
int B[][] = {{1,2}, {2,3,4}, {5, 6}};
|
Der zweite Fall lässt erkennen, dass das Feld nicht unbedingt rechteckig sein muss.
Das Pascal’sche Dreieck
Das folgende Beispiel zeigt eine weitere Anwendung von nicht rechteckigen Arrays, in dem das Pascal’sche Dreieck nachgebildet wird. Das Dreieck ist so aufgebaut, dass die Elemente unter einer Zahl genau die Summe der beiden direkt darüber stehenden Zahlen bilden. Die Ränder sind mit Einsen belegt.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
Abbildung 3.1
Das Pascal’sche Dreieck
Beispiel In der Implementierung wird zu jeder Ebene dynamisch ein Feld mit der passenden Länge angefordert. Die Ausgabe ist nicht – wie in der Grafik – zentriert.
|
Listing 3.5
Pascal.java
class Pascal
{
public static void main( String args[] )
{
int dreieck[][] = new int[7][];
for ( int i = 0; i < dreieck.length; i++ )
{
dreieck[i] = new int[i+1];
for ( int j = 0; j <= i; j++ )
{
if ( (j == 0) || (j == i) )
dreieck[i][j] = 1;
else
dreieck[i][j] = dreieck[i-1][j-1] + dreieck[i-1][j];
System.out.print( dreieck [i][j] + " " );
}
System.out.println();
}
}
}
Andere Anwendungen
Auf diese Art und Weise ist die Verwaltung von symmetrischen Matrizen einfach, denn eine solche Matrix enthält symmetrisch zur Diagonalen gleiche Elemente. Daher kann entweder die obere oder die untere Dreiecksmatrix entfallen. Besonders nützlich ist der Einsatz dieser effizienten Speicherform für Adjazenzmatrizen5
bei ungerichteten Graphen.
3.4.11 Die Wahrheit über die Array-Initialisierung
 
So schön die kompakte Initialisierung der Feldelemente ist, so laufzeit- und speicherintensiv ist sie. Da Java eine dynamische Sprache ist, passt das Konzept der Array-Initialisierung nicht ganz in das Bild. Und daher wird die Initialisierung auch erst zur Laufzeit durchgeführt. Bleiben wir bei unseren Primzahlen:
int primiMäuschen[] = { 1, 2, 3, 5, 7, 7+4 };
wird vom Java Compiler umgeformt und analog zu Folgendem behandelt:
int primiMäuschen[] = new int[6];
primiMäuschen[0] = 1;
primiMäuschen[1] = 2;
primiMäuschen[2] = 3;
primiMäuschen[3] = 5;
primiMäuschen[4] = 7;
primiMäuschen[5] = 11;
Erst nach etwas Überlegung wird das erschreckende Ausmaß sichtbar. Zunächst ist es der Speicherbedarf für die Methoden. Ist das Feld primiMäuschen beispielsweise in einer lokalen Methode untergebracht, so kostet die Zuweisung sehr viel Laufzeit, da wir viele Zugriffe haben, die auch alle schön durch die Index-Überprüfung gesichert sind. Da zudem der Bytecode für eine einzelne Methode wegen diverser Beschränkungen in der JVM nur beschränkt lang sein darf, kann dieser Platz für richtig große Arrays schnell erschöpft sein. Daher ist davon abzuraten, etwa Bilder oder große Tabellen im Programmcode zu speichern. Unter C war es populär, ein Programm einzusetzen, welches eine Datei in eine Folge von Array-Deklarationen verwandelte. Ist dies in Java wirklich nötig, dann sollten wir Folgendes in Betracht ziehen:
|
Wir verwenden ein statisches Feld (eine Klassenvariable), sodass das Array nur einmal während des Programmlaufs initialisiert werden muss. |
|
Sind die Werte im Byte-Bereich, so können wir diese in einen String konvertieren und später den String in ein Feld umwandeln. (Das ist eine sehr clevere Methode um Binärdaten einfach unterzubringen.) |
3.4.12 Arrays kopieren und füllen
 
Wollen wir eine Kopie eines Arrays mit gleicher Größe und gleichem Elementtyp schaffen, so nutzen wir dazu die Objektmethode clone(). Es klont – also in unserem Fall kopiert – die Elemente des Array-Objekts in ein neues. Die Kopie ist jedoch flach, was besagt, dass nur das Feld, aber nicht die referenzierten Objekte mit kopiert werden. Bei mehrdimensionalen Arrays wird also nur die erste Dimension kopiert, Unter-Arrays werden somit gemeinsam genutzt.
Beispiel Die clone()-Methode bei Feldern
int quelle[] = new int[6];
int ziel[] = (int [])quelle.clone();
|
Eine weitere nützliche Funktion ist die statische Funktion arraycopy() der Klasse System. System.arraycopy() arbeitet auf zwei schon existierenden Feldern. Somit muss, anders als bei clone(), zuerst ein (leeres) Array-Objekt geschaffen werden, das mindestens so lang (groß) ist wie das ursprüngliche Array. arraycopy() eignet sich dazu, sich vergrößernde Felder zu implementieren. Natürlich kann die Methode auch dazu verwendet werden, Elemente eines Felds um bestimmte Positionen zu verschieben:
final class java.lang.System
|
|
static void arraycopy( Object src, int src_pos,
Object dst, int dst_pos, int length )
Kopiert length Objekte des Arrays src ab der Position src_pos in ein Array dst an die Stelle dst_pos. |
Abbildung 3.2
Kopieren der Elemente von einem Feld in ein anderes
|
Beispiel Bewege alle Elemente eines Feldes f um eine Stelle nach links/rechts.
System.arraycopy( f, 1, f, 0, f.length-1 ); // links
System.arraycopy( f, 0, f, 1, f.length-1 ); // rechts
|
3.4.13 Mehrere Rückgabeparameter
 
Wenn wir in Java Funktionen schreiben, dann haben diese höchstens einen Rückgabewert. Wollen wir aber mehr als einen Wert zurückgeben, müssen wir eine andere Lösung suchen, die häufig so aussieht, dass ein Objekt die Rückgabewerte zusammenfasst. Solch ein Objekt kann auch ein Array sein. Betrachten wir eine Methode, die für zwei Zahlen die Summe und das Produkt liefert.
Listing 3.6
Prodsum.java
public class ProdSum
{
static int[] prodSum( int a, int b )
{
return new int[]{ a*b, a+b };
}
public static void main( String args[] )
{
System.out.println( prodSum(9,3)[1] );
}
}
3.4.14 Parameter per Referenz übergeben
 
Ob es nun Werte primitiver Typen oder Referenzen sind, in Java werden alle Parameter als Kopie per Wert übergeben. Wollen wir die Parameterübergabe per Referenz simulieren und somit der Methode erlauben, die übergebenen Parameterwerte nach außen hin sichtbar zu ändern, so ist es eine gute Lösung, ein Array zu benutzen.
Beispiel Über Felder Werte per Referenz übergeben
void foo()
{
int a[] = { 4 };
inc( a );
System.out.println( a[0] );
}
void inc( int a[] )
{
a[0]++;
}
|
3.4.15 Der Einstiegspunkt für das Laufzeitsystem
 
In Java-Klassen gibt es – ähnlich wie in C(++) – eine ausgezeichnete Funktion main(), die das Laufzeitsystem in der angegebenen Hauptklasse (oder Startklasse) des Programms aufruft. Die main()-Funktion ist für alle Klassen und in JVM zugänglich (public) und auf jeden Fall statisch (static) zu deklarieren. Die Methode muss statisch sein, da auch ohne Exemplar der Klasse ein Funktionsaufruf möglich sein soll. Als Parameter wird ein Array von Zeichenketten angenommen. In diesem sind die auf der Kommandozeile übergebenen Parameter gespeichert:
public static void main( String args[] )
Stimmt der Kopf der Methode nicht, dann wird diese Funktion nicht als Einstiegspunkt von der virtuellen Maschine erkannt.
Hinweis Im Gegensatz zu C(++) steht im ersten Element des Argument-Arrays mit Index 0 nicht der Programmname, also der Name der Hauptklasse, sondern bereits der erste Programmparameter der Kommandozeile.
|
Die Anzahl der Parameter
Eine besondere Variable für die Anzahl der Parameter ist nicht vonnöten, da das String-Array-Objekt selbst weiß, wie viele Parameter es enthält. Dem folgenden Programm können wir bei der Ausführung hinter dem Klassennamen noch einen Vornamen auf der Kommandozeile übergeben. Dieser wird dann auf der Standardausgabe ausgegeben. Wir können eine Schleife verwenden, um alle Kommandozeilenparameter auszugeben.
Listing 3.7
LiebtHamster.java
class LiebtHamster
{
public static void main( String args[] )
{
if ( args.length == 0 )
System.out.println( "Was!! Keiner liebt kleine Hamster?" );
else
{
System.out.print( "Liebt kleine Hamster: " );
for ( int i = 0; i < args.length; i++ )
System.out.print( args[i] + " " );
System.out.println();
}
}
}
Wir können das Programm auf der Kommandozeile wie folgt aufrufen:
$ java LiebtHamster Raphael Nathalie Regina Markus Mirjam
3.4.16 Der Rückgabewert von main()
 
Der Rückgabetyp void ist sicherlich diskussionswürdig, da die Sprachentwerfer auch hätten fordern können, dass ein Programm immer einen Statuscode an das aufrufende Programm zurückgibt. Für diese Lösung haben sie sich aber nicht entschieden, da Java-Programme in der Regel nur minimal mit dem umgebenden Betriebssystem interagieren sollen und echte Plattformunabhängigkeit gefordert ist, etwa bei Java in Handys. Für die Fälle, in denen ein Statuscode zurückgeliefert werden soll, steht die Funktion exit() der Klasse System zur Verfügung.
static void exit( int status )
Ein Aufruf von exit() beendet die aktuelle JVM und gibt das Argument der Methode als Statuswert zurück. Ein Wert ungleich Null zeigt einen Fehler an. Also ist der Rückgabewert beim normalen fehlerfreien Verlassen Null. Eine SecurityException wird geworfen, falls der aktuelle SecurityManager dem aufrufenden Code nicht erlaubt, JVM zu beenden. Das gilt insbesondere bei Applets in einem Webbrowser.
3.4.17 Die Klasse Arrays
 
Die Klasse java.util.Arrays definiert nützliche Funktionen im Umgang mit Arrays. So bietet sie statische Funktionen zum Sortieren und Füllen von Feldern sowie zur binären Suche.
Arrays.asList()
Nehmen wir an, wir haben es mit einem Feld von Hundenamen zu tun, welches wir auf dem Bildschirm ausgeben wollen.
String hundenamen[] = {
"Flocky Fluke", "Frizzi Faro", "Fanny Favorit",
"Frosty Filius", "Face Flash", "Fame Friscco"
};
Soll der Feldinhalt zum Testen auf den Bildschirm gebracht werden, so kommt eine Ausgabe mit System.out.println(hundenamen) nicht in Frage, denn toString() ist auf dem Objekttyp Array so nicht definiert und liefert:
[Ljava.lang.String;@111f71
Die Methode Arrays.asList() eignet sich gut dazu, ein Feld auszugeben:
System.out.println( Arrays.asList(hundenamen) );
Das spart eine for-Schleife, die durch das Feld läuft und auf jedem Element print() aufruft. Die Klasse Arrays liegt im Paket java.util.
1
Dies kommt in C++ allerdings wieder in Mode und macht auch Sinn. Aber dies ist eine andere Geschichte, die an anderer Stelle erzählt werden muss. In C++ gehören Typ-Konstruktionsoperatoren wie *, & und [] auch zu den deklarierten Variablen. In Java dagegen gibt es eben keine Variablen, die ein Array von Strings speichern, sondern es gibt nur Variablen vom Referenztyp »eindimensionales Array mit Elementtyp String«.
2
Obwohl er sich bei nicht initialisierten lokalen Variablen auch beschwert.
3
Ganz anders verhält sich da Perl. Dort wird ein negativer Index dazu verwendet, ein Feldelement relativ zum letzten Array-Eintrag anzusprechen. Und auch bei C ist ein negativer Index durchaus möglich und praktisch.
4
Die in PASCAL übliche Notation A[i,j] wird in Java nicht unterstützt. Das wäre im Prinzip möglich, da Java im Gegensatz zu C(++) den Kommaoperator nur in for-Schleifen zulässt. In C(++) gab die Schreibweise hübsche Fehler, die zur Übersetzungszeit nicht angezeigt wurden.
5
Eine Adjazenzmatrix stellt eine einfache Art dar, Graphen zu speichern. Sie besteht aus einem zweidimensionalem Array, das die Informationen über vorhandene Kanten im (gerichteten) Graphen enthält. Existiert eine Kante von einem Knoten zum anderen, so befindet sich in der Zelle ein Eintrag, entweder true/false für »Ja, die beiden sind verbunden« oder ein Ganzzahlwert für eine Gewichtung (Kantengewicht).
|