Arrays

<-- zurck zur Startseite | <- eine Seite vor | eine Seite weiter ->

  1. Was sind Arrays?
  2. Länge von Arrays abfragen
  3. mehrdimensionale Arrays
  4. Methoden der Klasse "Arrays"
  5. Kopieren von Arrays
  6. Übung
  7. Lösung

1. Was sind Arrays?

Array lässt sich ins Deutsche etwa mit "Anordnung" übersetzen. In Programmiersprachen sind Arrays eine "Sammlung" von Daten des selben Typs unter einheitlichem Namen, man spricht auch von einem Feld. Angenommen, wir wollten in einem Programm Kundennummern verwalten. Es wäre sehr mühsam, für jede einzelne Kundennummer eine eigene Variable anzulegen. Vor allem, wenn man sich alle Kundennummern später auf dem Bildschirm ausgeben lassen will, kommt man so schnell in Teufels Küche. Wünschenswert wäre es ergo, alle Kundennummern unter dem Namen "Kundennummern" anzusprechen:

int[] kundennummern = {1001, 1002, 1003, 1004};

Wie wir an diesem Beispiel sehen, erfolgt die Deklaration ähnlich wie bei normalen Variablen: zuerst wird der entsprechende Datentyp vorangestellt, in unserem Fall logischerweise "Integer". Um dem Compiler allerdings kenntlich zu machen, dass hier keine normale Variable folgt, werden hinter dem Datentyp eckige Klammern geschrieben. Nach der Initialisierung des Arrays werden die Werte zugewiesen. Wie bei Variablen kann dies zusammen mit der Deklaration erfolgen. Diese Art der Initialisierung ist uns bereits von den primitiven Datentypen her bekannt: man verwendet ein Literal. In diesem Fall wollen wir 5 Kundennummern speichern. Dadurch erhalten wir ein 4-elementiges Array. Natürlich kann ein Array aber auch nahezu jede beliebige anderen Größe annehmen. Die Werte, die gespeichert werden, können bei der erstmaligen Zuweisung direkt - zwischen geschweiften Klammern und durch Kommata getrennt - zugewiesen werden. Wird das Array erst später sukzessive mit Werten gefüllt, empfiehlt sich eine andere Vorgehensweise:

int[] kundennummern;
kundennummern = new int[4];
kundennummern[0] = 1001;
kundennummern[1] = 1002;
kundennummern[2] = 1003;
kundennummern[3] = 1004;
System.out.println("letze Kundennummer: " + kundennummern[3]);

Der C++-Kenner sieht sofort: Arrays werden in Java eigentlich genauso angelegt wie in C++, nur dass man keinen Zeiger mitangeben muss, wenn man die Größe des Arrays erst später, d.h. nach der Deklaration, festlegt. Hier wurden Deklaration und Initialisierung getrennt durchgeführt, wobei sie - steht die Größe schon bei der Deklaration fest - auch gemeinsam in einer Zeile durchgeführt werden können. Zur Angabe der gewünschten Größe bzw. der Anzahl der Werte, die das Array aufnehmen soll, dient das Schlüsselwort new, gefolgt von der Angabe des Datentyps (hier "int") und der Länge des Arrays in eckigen Klammern. Hier wird wieder ein Array mit 4 Elementen angelegt. Doch Vorsicht: die Größe des Arrays ist später nicht mehr veränderbar! Man sollte deshalb das Array erst anlegen, wenn man weiß, wie viele Einzelwerte man darin ablegen will.
Die einzelnen Positionen des Arrays können über einen Index angesprochen werden: er wird zwischen eckige Klammern hinter dem Arraynamen geschrieben. Ein Fehler, den Anfänger immer wieder machen, ist zu vergessen, dass der Rechner das Zählen bei 0 und nicht bei 1 beginnt. Das erste Element des Arrays hat also den Index 0. Bei einem 4-elementigen Array hat das letzte Element ergo den Index 3. Mathematisch ausgedrückt hat ein n-elementiges Array Indices von 0 bis n-1. Wie wir an diesem Beispiel sehen, kann über die Indices sowohl die einzelnen Werte zugewiesen als auch wieder abgefragt werden. Der Index selbst muss vom Typ "int" sein.
Was passiert aber, wenn man einen Index angibt, der außerhalb der Grenzen des Arrays liegt, z.B. "kundennummern[4]"? In C++ stürzt entweder das Programm ab oder es wird mit einer assert-Anweisung abgebrochen. In Java bleibt der Fehler allerdings kontrollierbar: es wird eine "Exception" (hier: ArrayIndexOutOfBoundsException) ausgelöst, die so abgefangen werden kann, dass das Programm weiterlaufen kann. Die Fehlerbehandlung bzw. Behandlung von Ausnahmen werden wir noch kennen lernen.

Um C++-Programmierern den Umstieg zu erleichtern, akzeptiert der Java-Compiler auch die C++-Schreibweise. Diese ist in Java jedoch unüblich und sollte deshalb vermieden werden:

int kundennummern[];

nach oben

2. Länge von Arrays abfragen

Das Besondere an Arrays in Java ist, dass sie Objekte sind. Auf Objekte werden wir später noch genauer eingehen, wichtig ist für uns in diesem Zusammenhang, dass Arrays damit auch "Eigenschaften" (sog. Instanz-Variablen) besitzen. Wird ein Array initialisiert, dann besitzt es automatisch eine Eigenschaft "Länge", die die Anzahl der Elemente im Array speichert. Bei einem Array mit 4 Elementen (und damit Indices von 0 bis 3) gibt diese Eigenschaft logischerweise die Zahl 4 zurück:

int[] kundennummern = new int[4];
System.out.println("Laenge des Arrays: " + kundennummern.length);

Die Länge des Arrays ist in "length" gespeichert: sie wird mit einem Punkt mit dem Arraynamen verbunden und so dem richtigen Array zugeordnet. Das dynamische Auffüllen und Ausgeben eines Arrays wird damit zum Kinderspiel:

int[] kundennummern = new int[4];
for (int i = 0; i < kundennummern.length; i++) 
   kundennummern[i] = 1001 + i;
for (int i = 0; i < kundennummern.length; i++)
   System.out.println("Kundennummer: " + (i+1) + ": " + kundennummern[i]);

Da die Länge des Arrays 4 ist, der höchste Index aber 3 beträgt, darf die for-Schleife nur solange durchlaufen werden, wie i gerade noch kleiner ist als die Länge des Arrays. i durchläuft jetzt Werte von 0 bis 3 und kann damit als Index für die Arrayelemente verwendet werden. Gleichzeitig wird auch die Kundennummer hochgezählt und bei jedem Durchlauf erhöht (beachte, dass i beim ersten Durchlauf noch den Wert 0 hat). Auf dieselbe Weise kann das Array auch wieder ausgelesen werden.
Spätestens hier sollte klar werden, wozu Arrays gut sind! Angenommen, wir erhöhen die Anzahl der Kundennummern von 4 auf 10: die einzige Änderung, die wir mit obiger Technik durchführen müssten, besteht bei der Initialisierung des Arrays. Der Rest bleibt gleich und wir haben uns viel Arbeit gespart.

nach oben

3. mehrdimensionale Arrays

Mehrdimensionale Arrays sind solche Arrays, bei denen jedes Array-Element seinerseits wieder ein Array enthält. Man erhält also ein Array aus Arrays. Damit lassen sich Matrixstrukturen datentechnisch abbilden. Wenn man anfangs Vorstellungsschwierigkeiten hat, sollte man nicht in Panik verfallen, in der Regel muss es sich erst einmal setzen. Die Deklaration und Initialisierung erfolgt wie bei eindimensionalen Arrays, wobei jede "Dimension" durch ein zusätzliches Paar eckiger Klammern angezeigt wird. Beispiel für ein zweidimensionales Array mit der Größe 4*3:

int[][] = new int[4][3];

Jedes der vier Arrayelemente besteht seinerseits wieder aus einem Array mit 3 Elementen. Der Zugriff erfolgt wiederum über die zugehörigen Indices, das gesamte mehrdimensionale Array kann durch geschachtelte Schleifen durchlaufen werden:

int[][] test = new int[4][3]; 
for (int i=0; i < test.length; i++) {
   for (int j=0; j < test[i].length; j++)
      test[i][j] = i*j;
}

In diesem Beispiel wird mit der Variable i zuerst das äußere Array durchlaufen, mit der Variable j die jeweils inneren. Es dürfte klar sein, dass "test[i].length" hier korrekt ist: da jedes Element des Arrays wieder ein Array ist, besitzt es auch die Eigenschaft "length".
Nach diesen Regeln kann ein Array auch in die dritte, vierte oder eine noch höhere Dimension erweitert werden. Des Weiteren muss ein mehrdimensionales Array keineswegs symmetrisch ausfallen. Im Gegensatz zu Pascal und Konsorten muss ein Array nicht quadratisch oder rechteckig sein. Folgendes Beispiel initialisiert ein mehrdimensionales, unsymmetrisches Array mit Fließkommawerten als Literal (beachte, dass dabei natürlich auch die geschweiften Klammern geschachtelt werden müssen):

double[][] test = { {5.5, 2.3},
                    {3.4},
                    {0.6, 3.0, 2.1}
                  };
for (int i=0; i < test.length; i++) {
   for (int j=0; j< test[i].length; j++)
      System.out.print(test[i][j] + "\t");
   System.out.println();
}

nach oben

4. Methoden der Klasse "Arrays"

Zum Glück sind wir bei der Handhabung von Arrays nicht alleine unserem programmiererischen Geschick ausgeliefert. Seit Java 2 (also dem JDK 1.2 und höher) gibt es die Klasse Arrays im Paket java.util, das uns für viele wichtige Bereiche statische Klassenmethoden bereitstellt. Bevor wir uns allerdings diese Hilfsmethoden ansehen, müssen wir noch klären, was es mit Klassen in Paketen auf sich hat.
In Java setzen sich mehrere Klassen zu einem Paket zusammen. Wie man selbst Pakete erstellt (welche man dann auch als Hilfsklassen missbrauchen kann), werden wir später noch kennen lernen, für den Moment interessiert uns mehr, wie wir die Pakete für uns nutzbar machen können. Dazu dienen import-Anweisungen am Anfang des Programms. Im Folgenden wissen wir, dass wir das Paket "java.util" (Java-Hilfsklassen) brauchen:

import java.util.*;

Zu beachten ist, dass die import-Anweisungen zu Beginn des Quelltextes angeführt werden. Dabei handelt es sich aber nicht um Präprozessor-Anweisungen wie "include" in C/C++, da ein solcher schlicht fehlt.
Der Asterisk (Sternchen) hinter dem Paketnamen signalisiert, dass wir alle Klassen aus diesem Paket importieren wollen. Es wären nun alle Klassen, die in diesem Paket vorhanden sind, in unserem Programm nutzbar, und damit auch deren Methoden. Allerdings kann man die Angabe spezifizieren, wenn man die genaue Klasse kennt, die man benötigt, also in unserem Fall die Klasse "Arrays". An die Stelle des Asterisk wird dann der Klassenname gesetzt:

import java.util.Arrays;

Nun könnte man versucht sein, vorsorglich mal alles zu importieren, was man so kennt. Das sollte man aber nicht tun, da das Programm irgendwann extrem langsam wird. Compiler und Virtual Machine müssen dann nämlich den ganzen Packen jeweils suchen und einbinden. Man sollte deshalb nur die Pakete und Klassen importieren, die man für seine Problemstellung auch tatsächlich braucht.

Wenden wir uns nun den Methoden der Klasse "Arrays" zu. Die wichtigstens Methoden sind sort, binarySearch, fill und equals, wobei man wohl in der Praxis am ehesten über "sort" stolpert, da Sortierungen in der Informatik eine herausragende Bedeutung besitzen. Zunächst die Definitionen der Methoden:

public static void sort(int[] a)
public static int binarySearch(int[] a, int key)
public static void fill(int[] a, int val)
public static boolean equals(int[] a, int[] a2)

Die Methode sort besitzt keinen Rückgabewert und sortiert das Array, das ihr übergeben wird, in aufsteigender Reihenfolge. Benötigt man das Array in absteigender Reihenfolge sortiert, so kann man sich damit behelfen, dass man das sortierte Array von hinten durchläuft, beispielsweise in einer for-Schleife von a.length-1 bis 0. Ansonsten gibt es eine weitere Variante für ein Array aus Objekten, dem man als zweites Argument ein Comparator-Argument übergeben kann, nach dem dann verglichen und sortiert wird. Diese Technik werden wir aber erst später bei den "Collections" durchnehmen.
Die Methode binarySearch führt eine sog. Binärsuche auf einem (sortierten) Array durch und gibt den Index zurück, an dem der Wert, er als zweites Argument übergeben wird, im Array gefunden wird (Wert ist negativ, wenn nichts gefunden wird, Interessierte können dazu einen Blick in die Java-Doku werfen). Die Binärsuche ahmt in gewissem Maße das menschliche Suchverhalten nach. Wenn man ein Telefonbuch aufschlägt, wird man nicht jeden einzelnen Eintrag nacheinander ansehen, sondern aufschlagen und dann anhand des Alphabets abschätzen, ob man zurückblättern oder weiterblättern muss. Das Intervall wird ständig verkleinert. Ebenso geht die Binärsuche vor: zuerst wird, die Mitte genommen und verglichen: ist sie grösser, dann wird im ersten Teil weitergesucht, andernfalls im zweiten. Dann wird wieder die Mitte genommen usw. Dieses Verfahren ist extrem effizient: bei n Elementen müssten normalerweise im schlimmsten Fall alle n Elemente angesehen werden, mit der Binärsuche wird das gesuchte Element aber auch im schlimmsten Fall nach nur log(n) Operationen gefunden! Dazu muss aber das Array, genau wie das Telefonbuch, sortiert vorliegen! Deswegen funktioniert die Methode "binarySearch" auch nur mit einem sortierten Array.
Die Methode fill ist dagegen vergleichsweise einfach, sie füllt das Array komplett mit dem als zweites Argument übergeben Wert auf.
Die Methode equals liefert einen booleschen Wert zurück und vergleicht zwei Arrays. Sind sie identisch, ist der Rückgabewert "true", ansonsten "false".
Exemplarisch wurden hier die Versionen herausgegriffen, die ein Integer erwarten. Natürlich gibt es auch Methoden, die genauso angesprochen werden können, nur statt einem Integer-Array als Argument ein Array aus "byte", "short", "char", "long", "float" oder "double" erwarten. Auch ein Array aus Objekten kann übergeben werden. Die Methoden "fill" und "equals" akzeptieren auch Arrays aus booleschen Werten.

Das soll für den Moment genügen, sehen wir uns nun ein Beispiel an, damit die Verwendung dieser Methoden klar wird.

import java.util.Arrays;

public class ArrayTest1 {
  public static void main(String[] args) {
     int toFind = 50;
     int[] a = new int[100];
     int[] b = new int[100];
     Arrays.fill(a, 2);
     Arrays.fill(b, 2);
     if (Arrays.equals(a, b))
           System.out.println("Arrays a und b sind identisch!");
     for (int i=0; i<a.length; i++)
           a[i] = (int)(Math.random()*100);
     Arrays.sort(a);
     int index = Arrays.binarySearch(a, toFind);
     if (index < 0)
           System.out.println("Wert " + toFind + " nicht im Array enthalten!");
     else
           System.out.println("Wert " + toFind + " an der Stelle " + index + " gefunden!");
  }
}

Zunächst werden zwei Arrays a und b mit je 100 Elementen angelegt und jeweils mittels der Methode "fill" komplett mit dem Wert 2 aufgefüllt. Daher sind die beiden Arrays auch bei der anschließenden Prüfung gleich: die Methode "equals" liefert "true" zurück, der boolesche Wert kann in einer Bedingung verarbeitet werden. Daraufhin wird das Array a mit Zufallswerten zwischen 0 und 100 aufgefüllt (Math.random() liefert ja Werte zwischen 0 und 1, diese müssen mit 100 multipliziert und anschließend, da es sich um "double"-Werte handelt, auf Integer gecastet werden). Nachdem das Array mit der Methode "sort" sortiert wurde, kann die Binärsuche angewendet werden: die Methode "binarySearch" bekommt als zweites Argument die Integer-Variable "toFind" übergeben, die die Zahl enthält, die im Array gesucht wurde. Ist der zurückgelieferte Index negativ, gibt es keine Vorkommen dieser Zahl im Array, andernfalls wird der Index/die Position des gefundenen Vorkommens ausgegeben. Aber Achtung: die Binärsuche gibt aufgrund ihrer Vorgehensweise nicht zwangsläufig das erste Vorkommen einer Zahl von vorne gesehen zurück, sondern den Index, an dem die Binärsuche zuerst fündig wird!

nach oben

5. Kopieren von Arrays

Ein weiteres Werkzeug zur Bearbeitung von Arrays stellt uns die Klasse System mit der Methode arraycopy zur Verfügung, welche das Umkopieren von Arrays bzw. Teilen davon erlaubt:

public static native void arraycopy(Object src, int src_position, Object dst, int dst_position, int length)

Diese Methode ist anfangs vielleicht etwas verwirrend, aber gehen wir alles der Reihe nach durch:
Zunächst werden der Methode das Quellarray "src" und die Startposition "src_position" des zu kopierenden Teils übergeben. Man sollte in der Methodendefinition nicht vom erwarteten Datentyp "Object" verwirren lassen, denn ein Array ist in Java ja auch ein Objekt. Argument drei und vier stellen das Zielarray "dst", in das der Teil aus dem Quellarray kopiert werden soll, und die Startposition "dst_position" im Zielarray dar. Der aus dem Quellarray kopierte Teil wird im Zielarray an dieser Stelle so eingefügt, dass die vorherigen Elemente überschrieben werden. Das fünfte Argument "length" schließlich beschreibt die Länge des zu kopierenden Bereichs bzw. die Anzahl der zu kopierenden Elemente. Er darf nicht über die Grenzen des Quell- und Zielarrays hinausgehen, ansonsten wird wieder eine "Exception" ausgelöst. Die Art der Ausnahme ist hier wieder ArrayIndexOutOfBoundsException, also eine Ausnahme erzeugt durch überschreiten der Array-Grenzen.
Das Schöne an dieser Methode ist, dass Quell- und Zielarray auch identisch sein können.
(NB: der Zusatz native bedeutet, dass diese Methode nicht in Java geschrieben wurde, sondern sog. "native-code" eingebettet wurde. Konkret bedeutet dies in diesem Fall, dass die Methode aus Performance-Gründen in C++ und Assembler geschrieben wurde, also extrem maschinennah)
Für diese Methode brauchen wir die Klasse "Arrays" nicht einzubinden, da sie zur Klasse "System" gehört. Ein kleines Beispiel zum Abschluss:

public class ArrayTest2 {
    public static void main(String[] args) {
        int[] a = {1, 2, 5, 8, 9};
        int[] b = {3, 4};
        System.arraycopy(a, 2, a, 4, 1);
        System.arraycopy(b, 0, a, 2, 2);
        for (int i=0; i<a.length; i++)
              System.out.println("Element " + i + ": " + a[i]);
    }
}

Gegeben seien die beiden Arrays a und b. Das Array a soll nachher die Zahlen 1 bis 5 in aufsteigender Reihenfolge enthalten (freilich gibt es einfachere Lösungen hierfür, es soll nur die Funktionsweise zeigen). Dazu wird zunächst die 9 am Ende des Arrays a durch eine 5 ersetzt. Dazu wird aus dem Quellarray a die Zahl 5 an der Indexposition 2 genommen und ins Zielarray a an die Indexposition 4 gesetzt, also das Ende des 5-elementigen Arrays. Da wir nur ein Element kopieren, ist die Länge 1. Jetzt wird das gesamte Quellarray b (daher ist die Startposition bei 0) in das Zielarray a kopiert, und zwar an die Indexposition 2. Die Länge 2 bewirkt, dass das gesamte Array b umkopiert wird und somit die Zahlen 5 und 8 in der Mitte des Arrays a durch 3 und 4 ersetzt werden. Von der Korrektheit des Ergebnisses können wir uns nun leicht überzeugen.

nach oben

6. Übung

Gegeben sei folgendes Array:

double[] myArray = {25.0, 10.4, 18.9, 36.6, 9.0, 10.0, 34.2, 22.1, 13.7, 17.3};

Sortiere das Array und ermittle den Index, an dem nach der Sortierung die Zahl 22.1 zu finden ist. Kopiere diese Zahl anschließend an die erste Stelle des Arrays mit Hilfe der Methode "arraycopy".

nach oben

7. Lösung

import java.util.Arrays;

public class SpielMitArrays {

  public static void main(String[] args) {
    double[] myArray = {25.0, 10.4, 18.9, 36.6, 9.0, 10.0, 34.2, 22.1, 13.7, 17.3};
    
    // Array sortieren und Wert 22.1 suchen
    Arrays.sort(myArray);
    int pos = Arrays.binarySearch(myArray, 22.1);
    System.out.println("22.1 wurde an der Position " + (pos+1) + " gefunden!");
    
    // Wert 22.1 an die erste Stelle umkopieren und Arrayelemente anzeigen lassen
    System.arraycopy(myArray, pos, myArray, 0, 1);
    System.out.println("Das Array nach dem Kopieren: ");
    for (int i=0; i<myArray.length; i++)
        System.out.print(myArray[i] + " ");
    System.out.println();
  }

}

Dieses Beispiel sollte eigentlich selbsterklörend sein. Einzig der Aufruf von "arraycopy" könnte noch Verwirrung stiften. Die Parameter der Reihe nach: Quell-Array ist "myArray", Elemente sollen ab der Position "pos" herauskopiert werden. Das Ziel des Kopierens ist wiederum "myArray", und zwar die erste Stelle, also der Index 0. Da nur ein Element kopiert werden soll, ist auch die Lönge (5. Parameter) nur 1.

nach oben