4.4 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. Dieser Abschnitt lehrt, wie Objekte für mehrdimensionale Arrays initialisiert, aufgebaut und genutzt werden.
Mehrdimensionale Array-Objekte mit new aufbauen
Die folgende Zeile deklariert ein zweidimensionales Array mit Platz für insgesamt 32 Zellen, die in vier Zeilen und acht Spalten angeordnet sind:
int[][] A = new int[ 4 ][ 8 ];
Obwohl mehrdimensionale Arrays im Prinzip Arrays mit Arrays als Elementen sind, lassen sie sich leicht deklarieren.
[+] Tipp
Zwei alternative Deklarationen (die Position der eckigen Klammern ist verschoben) sind:
int A[][] = new int[ 4 ][ 8 ];
int[] A[] = new int[ 4 ][ 8 ];
Es empfiehlt sich, alle eckigen Klammern hinter den Typ zu setzen.
Anlegen und Initialisieren in einem Schritt
Ebenso wie bei eindimensionalen Arrays lassen sich mehrdimensionale Arrays gleich beim Anlegen initialisieren:
Der zweite Fall lässt erkennen, dass das Array nicht unbedingt rechteckig sein muss. Dazu gleich mehr.
Zugriff auf Elemente
Einzelne Elemente spricht der Ausdruck A[i][j] an.[ 124 ](Die in Pascal übliche Notation A[i,j] wird in Java nicht unterstützt. Die Notation wäre im Prinzip möglich, da Java im Gegensatz zu C(++) den Komma-Operator nur in for-Schleifen zulässt. ) Der Zugriff erfolgt mit so vielen Klammerpaaren, wie die Dimension des Arrays angibt.
[zB] Beispiel
Der Aufbau von zweidimensionalen Arrays (und der Zugriff auf sie) ist mit einer Matrix bzw. Tabelle vergleichbar. Dann lässt sich der Eintrag im Array 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]
...
length bei mehrdimensionalen Arrays
Nehmen wir eine Buchstabendefinition wie die folgende:
char[][] letter = { { ' ', '#', ' ' },
{ '#', ' ', '#' },
{ '#', ' ', '#' },
{ '#', ' ', '#' },
{ ' ', '#', ' ' } };
Dann können wir length auf zwei verschiedene Weisen anwenden:
letter.length ergibt 5, denn es gibt fünf Zeilen.
letter[0].length ergibt 3 – genauso wie letter[1].length usw. –, weil jedes Unter-Array die Größe 3 hat.
Zweidimensionale Arrays mit ineinander verschachtelten Schleifen ablaufen
Um den Buchstaben unseres Beispiels auf dem Bildschirm auszugeben, nutzen wir zwei ineinander verschachtelte Schleifen:
for ( int line = 0; line < letter.length; line++ ) {
for ( int column = 0; column < letter[line].length; column++ )
System.out.print( letter[line][column] );
System.out.println();
}
Fassen wir das Wissen zu einem Programm zusammen, das vom Benutzer eine Zahl erfragt und diese Zahl in Binärdarstellung ausgibt. Wir drehen die Buchstaben um 90 Grad im Uhrzeigersinn, damit wir uns nicht damit beschäftigen müssen, die Buchstaben horizontal nebeneinanderzulegen.
package com.tutego.insel.array;
import java.util.Scanner;
public class BinaryBanner {
static void printLetter( char[][] letter ) {
for ( int column = 0; column < letter[0].length; column++ ) {
for ( int line = letter.length - 1; line >= 0; line-- )
System.out.print( letter[line][column] );
System.out.println();
}
System.out.println();
}
static void printZero() {
char[][] zero = { { ' ', '#', ' ' },
{ '#', ' ', '#' },
{ '#', ' ', '#' },
{ '#', ' ', '#' },
{ ' ', '#', ' ' } };
printLetter( zero );
}
static void printOne() {
char[][] one = { { ' ', '#' },
{ '#', '#' },
{ ' ', '#' },
{ ' ', '#' },
{ ' ', '#' } };
printLetter( one );
}
public static void main( String[] args ) {
int input = new Scanner( System.in ).nextInt();
String bin = Integer.toBinaryString( input );
System.out.printf( "Banner für %s (binär %s):%n", input, bin );
for ( int i = 0; i < bin.length(); i++ )
switch ( bin.charAt( i ) ) {
case '0': printZero(); break;
case '1': printOne(); break;
}
}
}
Die Methode printLetter(char[][]) bekommt als Argument das zweidimensionale Array und läuft anders ab als im ersten Fall, um die Rotation zu realisieren. Mit der Eingabe »2« gibt es folgende Ausgabe:
Banner für 2 (binär 10):
#
#####
###
# #
###
4.4.1 Nichtrechteckige Arrays *
Da in Java mehrdimensionale Arrays als Arrays von Arrays implementiert sind, müssen diese Arrays nicht zwingend rechteckig sein. Jede Zeile im Array kann eine eigene Größe haben.
[zB] Beispiel
Ein dreieckiges Array mit Zeilen der Länge 1, 2 und 3:
int[][] a = new int[ 3 ][];
for ( int i = 0; i < 3; i++ )
a[ i ] = new int[ i + 1 ];
Initialisierung der Unter-Arrays
Wenn wir ein mehrdimensionales Array deklarieren, erzeugen versteckte Schleifen automatisch die inneren Arrays. Bei
int[][] a = new int[ 3 ][ 4 ];
erzeugt die Laufzeitumgebung die passenden Unter-Arrays automatisch. Dies ist bei
int[][] a = new int[ 3 ][];
nicht so. Hier müssen wir selbst die Unter-Arrays initialisieren, bevor wir auf die Elemente zugreifen:
for ( int i = 0; i < a.length; i++ )
a[ i ] = new int[ 4 ];
PS: int[][] m = new int[][4]; funktioniert natürlich nicht!
[zB] Beispiel
Es gibt verschiedene Möglichkeiten, ein mehrdimensionales Array zu initialisieren:
int[][] A3x2 = { {1,2}, {2,3}, {3,4} };
oder
int[][] A3x2 = new int[][]{ {1,2}, {2,3}, {3,4} };
oder
int[][] A3x2 = new int[][]{ new int[]{1,2}, new int[]{2,3}, new int[]{3,4} };
Das pascalsche Dreieck
Das folgende Beispiel zeigt eine weitere Anwendung von nichtrechteckigen Arrays, in der das pascalsche Dreieck nachgebildet wird. Das Dreieck ist so aufgebaut, dass die Elemente unter einer Zahl genau die Summe der beiden direkt darüberstehenden 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
In der Implementierung wird zu jeder Ebene dynamisch ein Array mit der passenden Länge angefordert. Die Ausgabe tätigt printf(…) mit einigen Tricks mit dem Formatspezifizierer, da wir auf diese Weise ein führendes Leerzeichen bekommen:
class PascalsTriangle {
public static void main( String[] args ) {
int[][] triangle = new int[7][];
for ( int row = 0; row < triangle.length; row++ ) {
System.out.print( new String( new char[(14 - row * 2)] ).replace( '\0', ' ' ) );
triangle[row] = new int[row + 1];
for ( int col = 0; col <= row; col++ ) {
if ( (col == 0) || (col == row) )
triangle[row][col] = 1;
else
triangle[row][col] = triangle[row - 1][col - 1] +
triangle[row - 1][col];
System.out.printf( "%3d ", triangle[row][col] );
}
System.out.println();
}
}
}
Die Anweisung new String( new char[(14 - row * 2)] ).replace( '\0', ' ' ) produziert Einrückungen und greift eine fortgeschrittene API auf. (14 - row * 2) ist die Größe des standardmäßig mit 0 initialisierten Arrays, das dann an den Konstruktor der Klasse String übergeben wird, der wiederum ein String-Objekt aus dem char-Array aufbaut. Die replace(…)-Methode auf dem frischen String-Objekt führt wieder zu einem neuen String-Objekt, in dem alle '\0' -Werte durch Leerzeichen ersetzt sind.
Andere Anwendungen
Mit zweidimensionalen Arrays ist die Verwaltung von symmetrischen Matrizen einfach, da eine solche Matrix symmetrisch zur Diagonalen gleiche Elemente enthält. Daher kann entweder die obere oder die untere Dreiecksmatrix entfallen. Besonders nützlich ist der Einsatz dieser effizienten Speicherform für Adjazenzmatrizen[ 125 ](Eine Adjazenzmatrix stellt eine einfache Art dar, Graphen zu speichern. Sie besteht aus einem zweidimensionalen 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). ) bei ungerichteten Graphen.
[»] Hinweis
Eine ungewöhnliche Syntax in Java erlaubt es, bei Array-Rückgaben das Paar eckiger Klammern auch hinter den Methodenkopf zu stellen, also statt
static int[] productAndSum( int a, int b )
alternativ Folgendes zu schreiben:
static int productAndSum( int a, int b )[]
Das wird nicht empfohlen. Selbst so etwas wie int[] transposeMatrix(int[][] m)[] ist möglich, wird aber sinnvollerweise als int[][] transposeMatrix(int[][] matrix) geschrieben.