Zielsetzung
In der Programmierung stehen wir oft vor der Herausforderung, große Datenmengen zu organisieren. Das Speichern von Testergebnissen für hundert Studenten oder Preisen für tausende Produkte in einzelnen Variablen wäre mühsam, fehleranfällig und ineffizient. Arrays bieten eine Lösung.
Ein Array ist eine grundlegende Datenstruktur, die es uns ermöglicht, eine Sammlung von Elementen desselben Datentyps unter einem Namen zu speichern und zu verwalten. Ob ganze Zahlen, Dezimalzahlen, Zeichenketten oder benutzerdefinierte Objekte – Arrays bieten eine strukturierte Möglichkeit, diese Daten linear und direkt zugänglich zu halten.
Arrays sind besonders wichtig für die Entwicklung von Algorithmen. Sie sind die Grundlage für verschiedene Operationen, insbesondere für Such- und Sortierverfahren. Ohne Arrays oder eng verwandte Datenstrukturen wie Listen und Vektoren, die oft auf Arrays aufbauen, wäre die effiziente Implementierung vieler komplexer Algorithmen unmöglich.
Auf dieser Seite lernen Sie Arrays kennen. Wir werden ihre Deklaration, Initialisierung, den Zugriff auf Elemente und die damit verbundenen Operationen behandeln.
Inhalt dieser Seite
4.1.1 Einführendes Beispiel
Eine Java-Methode soll die kleinere von zwei int-Zahlen zurückliefern. Hier eine spontane Lösungsidee:
Programm 1
public int mini(int a, int b)
{
if (a < b)
return a;
else
return b;
}
Wenn der erste Parameter (a) kleiner ist als der zweite (b), wird er als die "kleinste Zahl" interpretiert und zurückgeliefert. Ist a jedoch nicht kleiner als b, wird b als "kleinste Zahl" zurückgeliefert, selbst dann, wenn b genau so groß ist wie a.
Nun wollen wir die Methode so erweitern, dass das Minimum von drei Zahlen zurückgeliefert wird.
Programm 2
public int mini(int a, int b, int c)
{
int mini = a;
if (b < mini) mini = b;
if (c < mini) mini = c;
return mini;
}
Hier wird zunächst "behauptet", dass a die kleinste Zahl ist. Dann wird überprüft, ob b vielleicht kleiner ist als a. Wenn dies der Fall ist, dann wird b als kleinste Zahl angesehen (mini = b). Schließlich wird überprüft, ob c kleiner ist als das bisherige Miniumum. Wenn das der Fall ist, wird c als Minimum angesehen (mini = c). Am Ende wird dann die lokale Variable mini als Wert zurückgegeben.
Aufgabe 4.1.1 #1
Erläutern Sie die Funktionsweise der folgenden Methode:
public int betterMini(int a, int b, int c, int d)
{
return mini(mini(a,b),mini(c,d));
}
Wenn man nun das Minimum von fünf oder sechs Zahlen ermitteln will, kann man dieses Verfahren natürlich weiterhin anwenden. Der Quelltext wird dadurch aber immer komplexer und unübersichtlicher. Und wenn Sie mal das Minimum von 100 Zahlen bestimmen wollen, was machen Sie dann?
Programm 3
Hier kommen jetzt Arrays ins Spiel. Schauen wir uns doch einfach mal einen Quelltext an, der zunächst ein Array aus 100 int-Zahlen erzeugt, dann mit Zufallszahlen füllt und schließlich nach der kleinsten der 100 Zahlen sucht.
import java.util.Random;
public class ArrayTest
{
// Das Array wird deklariert
int[] hundertZahlen;
public ArrayTest()
// Das Array wird initialisiert
{
hundertZahlen = new int[100];
}
// Das Array wird mit 100 Zufallszahlen gefüllt
public void erzeugeArray()
{
Random wuerfel = new Random();
for (int i=0; i<100; i++)
hundertZahlen[i] = wuerfel.nextInt(1000)+1;
}
// Es wird im Array nach der kleinsten Zahl gesucht
public int mini()
{
int min = hundertZahlen[0];
for (int i=1; i<100; i++)
if (hundertZahlen[i] < min)
min = hundertZahlen[i];
return min;
}
}
Das ist schon ein recht langes Programm.
Aufgabe 4.1.1 #1
Analysieren Sie die Methode mini() und stellen Sie den Programmfluss mit einem Flussdiagramm graphisch dar.
⇒ Kopieren Sie sich diesen Quelltext in die Zwischenablage und fügen Sie ihn dann als Klasse in ein neues BlueJ-Projekt ein.
⇒ Legen Sie ein Objekt der Klasse an und probieren Sie die Methoden aus.
⇒ Untersuchen Sie das Objekt mit dem Objektinspektor.
Das erzeugte Objekt wird angeklickt
Dieses Bild zeigt, was passiert, wenn man das erzeugte Objekt der Klasse in BlueJ anklickt. Im Objektinspektor sehen wir nur eine einzige Variable, nämlich das Array mit dem Namen hundertZahlen.
Array-Variablen sind Referenzen
Bereits in den vorherigen Folgen hatten wir über Referenz-Variablen gesprochen, die keine richtigen Werte speichern, sondern "nur" die Adressen solcher Daten. Auch eine Array-Variable wie hundertZahlen ist eine solche Referenz-Variable.
Wenn wir diese Variable mit System.out.println() in die Konsole ausgeben, dann können wir die Speicheradresse direkt sehen, zum Beispiel [I@78c75e93 auf einem Apple-Rechner. Auf einem Windows- oder Linux-Rechner wird es ähnlich aussehen, wenn man die Adresse mit System.out.println() ausgibt.
Mit Hilfe des Objektinspektors von BlueJ können wir uns diese 100 Zahlen ansehen, die in dem Array gespeichert sind. Dazu reicht ein Doppelklick auf den gebogenen Pfeil:
Der Objektinspektor zeigt die Belegung des Arrays hundertZahlen
Wir sehen hier allerdings nicht alle 100 Werte, dafür ist das Fenster des Objektinspektors nicht lang genug. Wenn Sie zufällig vor einem senkrecht ausgerichteten 5K-Monitor sitzen, können Sie dieses Fenster ja in die Länge ziehen und dann vielleicht 60 oder 70 Elemente sehen. Aber wer bringt schon einen 5K-Monitor mit in die Vorlesung? Ach ja, der Typ da in der Mitte der zweiten Reihe:
Studierende mit ihren Laptops
Autor: ChatGPT, Lizenz: Public domain.
Nachdem Sie sich davon überzeugt haben, dass das Programm einwandfrei funktioniert, wollen wir es jetzt mal gründlicher untersuchen.
4.1.2 Gründliche Analyse des Programms↑
Erzeugen eines Arrays aus 100 int-Zahlen
Betrachten wir zunächst die ersten Zeilen des Programms. Die Zeile 1 wurde hier noch nicht berücksichtigt, darauf gehen wir später ein.
Die Instanzvariable hundertZahlen und der Konstruktor
Deklaration des Arrays
In Zeile 5 finden wir die Deklaration des Arrays hundertZahlen. Wir teilen dem Programm damit mit, dass wir ein Array aus int-Variablen benötigen, der als "hundertZahlen" bezeichnet werden soll.
Bei dieser Deklaration des Arrays wird aber noch nicht gesagt, wie viele Zahlen im Arbeitsspeicher untergebracht werden sollen. Am Ende von Zeile 5 weiß das Programm nur, dass wir nicht eine einzelne int-Zahl haben wollen, sondern ein Array von mehreren int-Zahlen. Das können zwei Zahlen sein, drei oder vier, hundert oder tausend oder noch mehr. Wenn wir versuchen, uns den Wert von hundertZahlen anzusehen, erhalten wir als Ergebnis den Wert null. Das heißt so viel wie: Es ist noch keine Adresse in hundertZahlen gespeichert.
Initialisierung des Arrays
Um tatsächlich Speicherbereich für die 100 Zahlen zu reservieren, müssen wir das Array initialisieren:
hundertZahlen = new int[100];
Jetzt wird im Arbeitsspeicher ein Bereich der Größe 100 x 32 Bit reserviert, der Platz für 100 int-Zahlen hat und unter einer Adresse erreicht werden kann, die in der Variablen hundertZahlen gespeichert ist.
Füllen des Arrays mit 100 Zufalls-Zahlen
Einen Zufallsgenerator erzeugen
Wenn wir sogenannte Zufallszahlen benötigen, dann müssen wir die Klasse Random in unseren Java-Quelltext einbinden. Das geschieht in der ersten Zeile des Programms:
import java.util.Random;
Die Klasse Random stellt einige wichtige Methoden zur Erzeugung von zufälligen Zahlen zur Verfügung. Damit wir auf diese Methoden zugreifen können, müssen wir zuerst ein Objekt dieser Klasse erzeugen. Das geschieht mit der Anweisung:
Random wuerfel = new Random();
Das Objekt der Klasse Random haben wir hier einfach wuerfel genannt.
Zufallszahlen erzeugen
Die Zeilen 12 bis 17 des Programms erzeugen ein mit 100 Zufallszahlen gefülltes Array:
Die Instanzvariable hundertZahlen und der Konstruktor
Zunächst wird ein Objekt wuerfel der Klasse Random erzeugt. Alle Random-Objekte besitzen eine Methode nextInt(N), die eine Zahl zwischen 0 und N-1 erzeugt - eine Zufallszahl. Da wir Zahlen größer als 0 benötigen, addieren wir einfach eine 1 zu jeder Zufallszahl dazu. So erhalten wir jetzt Zufallszahlen zwischen 1 und 1000.
Alternativ hätte man hier auch auf die Methode Math.random() zurückgreifen können, die eine double-Zahl zwischen 0 und 1 (exklusiv) erzeugt. Mit 1000 multipliziert und dann mit Typecasting in eine int-Variable verwandelt, hätte man das Gleiche erreicht:
hundertZahlen[i] = (int) (Math.random()*1000) + 1;
Dann hätte man kein Objekt wuerfel der Klasse Random anlegen müssen und hätte sich überhaupt den Import der Klasse Random gespart.
In der for-Schleife wird nun jedem der 100 Arrayelemente eine solche Zufallszahl zugewiesen. Man kann nach der Ausführung dieser Methode noch einmal den Objektinspektor aufrufen und sich das Array ansehen:
Die ersten 13 Element des Arrays mit ihren Zufallszahlen
Suche nach der kleinsten Zahl in dem Array
Bei der Suche nach der kleinsten Zahl gehen wir nach dem gleichen Algorithmus vor wie in dem Programm 2 weiter oben.
Zunächst behaupten wir in Zeile 21 (siehe Bild unten), dass die erste Zahl des Arrays, also hundertZahlen[0], die kleinste Zahl ist. Diese Zahl speichern wir in der lokalen Variablen min.
int min = hundertZahlen[0];
Dann untersuchen wir in der for-Schleife die nächsten 99 Zahlen. Jede Zahl wird mit min verglichen. Sobald ein Arrayelement hundertZahlen[i] kleiner ist als min, wird der Wert von min mit dieser neuen Zahl überschrieben:
min = hundertZahlen[i];
So machen wir das immer weiter, bis wir am Ende des Arrays angekommen sind:
Die sondierende Methode mini()
Theorieteil:
4.1.3 Was ist überhaupt ein Array?↑
Ein Array ist eine endliche Menge von Variablen des gleichen Datentyps. Unser Array aus dem obigen Programm besteht aus 100 int-Variablen. Genau so gut könnte man ein Array erzeugen, das aus 200 double-Variablen oder 2127 String-Variablen besteht. Das String-Array wäre dann ein Beispiel für Objekt-Arrays. Denn ein Array kann alle möglichen Datentypen enthalten, auch Objekte von Klassen - allerdings gilt auch für Objekt-Arrays, dass alle Objekte den gleichen Datentyp haben müssen, also der gleichen Klasse angehören müssen.
Objektarrays
Mit diesem Thema beschäftigen wir uns näher in der Folge 4.2. Aber für die Experten unter Ihnen schon mal ein kleiner Hinweis. Die Aussage, dass alle Elemente eines Objektarrays der gleichen Klasse angehören müssen, ist nicht ganz korrekt. Richtiger ist vielmehr, dass alle Objekte der gleichen Klassenhierarchie angehören müssen. Wenn von einer Oberklasse A drei verschiedene Unterklassen X, Y, Z existieren, dann kann ein Objektarray A[] vier verschiedene Objekttypen speichern, nämlich Objekte der Oberklasse A sowie der drei Unterklassen X, Y und Z.
Wie deklariert man ein Array?
Ein Array wird in einer Java-Klasse ähnlich wie eine normale Instanzvariable deklariert. Vergleichen wir einmal die Deklaration einer einzelnen int-Variable und eines Arrays aus lauter int-Variablen:
int eineZahl; int[] achtZahlen;
Das Array heißt zwar achtZahlen, die Arraygröße ist jedoch noch nicht festgelegt; das geschieht erst bei der Initialisierung des Arrays (s.u.). Auf die gleiche Weise könnte man auch ein Array aus double-Zahlen oder Strings deklarieren:
double[] zwanzigZahlen; String[] textseite;
Was passiert bei der Deklaration?
Bei der Deklaration teilt man dem Rechner bzw. dem Java-System mit, dass man ein Array haben möchte, und wie das Array heißen soll. Nähere Angaben, vor allem wie viele Elemente das Array haben soll, hat man noch nicht gemacht.
Nach der Deklaration
int[] achtZahlen;
hat man zwar eine Variable erzeugt, die "achtZahlen" heißt, aber in der Variable steht noch nicht die Adresse des zu erzeugenden Arrays, sondern der Wert null (was so viel heißt wie "noch keine Adresse vorhanden").
Wie initialisiert man ein Array?
Bei der Initialisierung wird die Zahl der gewünschten Arrayelemente festgelegt.
achtZahlen = new int[8];
Offensichtlich will man hier acht int-Zahlen verwenden.
Arrayvariablen sind Referenzen
Die Variable achtZahlen zeigt jetzt auf einen bestimmten Bereich im Arbeitsspeicher, der so groß ist, dass genau acht int-Zahlen untergebracht werden können. Die erste Speicherzelle dieses Bereichs hat eine bestimmte Adresse, und diese Startadresse wird jetzt in der Variable achtZahlen gespeichert. Das kann man leicht ausprobieren.
public class KleinerTest
{
int[] achtZahlen;
public KleinerTest()
{
achtZahlen = new int[8];
System.out.println(achtZahlen);
}
}
Ausgegeben wird jetzt ein Wert wie [I@38638550. Das ist die Darstellung der Speicheradresse für das Array auf einem Apple-Rechner mit M1-Prozessor unter MacOS Ventura. Bei einem Windows- oder Unix-Rechner wird es ähnlich aussehen.
Wie weist man einem Arrayelement Werte zu?
Ein Beispiel
achtZahlen[0] = 1; achtZahlen[1] = 4; achtZahlen[2] = 9;
Zur Zuweisung eines Wertes an ein Arrayelement benötigt man die relative Adresse dieses Elementes in dem Speicherbereich, in dem das Array liegt. Dazu dient der Index. Auf jedes Arrayelement kann direkt mithilfe des Index zugegriffen werden. Das erste Arrayelement hat immer den Index 0.
Weitere Beispiele mit ausführlichen Erläuterungen
zahl[0] = 13;
"zahl" ist hier der Bezeichner, 0 der Index, und 13 der Wert.
temperatur[12] = 27.3;
Hier heißt der Bezeichner "temperatur", der Index ist 12, und der zugewiesene Wert für das dreizehnte (!) Arrayelement ist 27.3.
inventar[2] = new Gegenstand("Hammer",0,45,2);
Der Bezeichner heißt hier "inventar", der Index ist 2, und der Wert für das dritte Element des Arrays ist nichts anderes als ein neues Objekt der Klasse Gegenstand. Dieses neue Objekt wird in dieser Programmzeile erst erzeugt, das sieht man ja an dem Wort "new" und dem Aufruf des Konstruktors der Klasse Gegenstand.
for (int k=0; k<max; k++) liste[k] = k*k+3*k-2;
Hier haben wir ein schönes Beispiel, wie man mit Hilfe von for-Schleifen (oder anderen Schleifen) den Umgang mit Arrays erleichtern kann.
Wie greift man auf den Wert von Arrayelementen zu?
Hier ein paar Beispiele
System.out.println(zahl[3]);
Ein Arrayelement kann mit dem println()-Befehl ausgegeben werden.
int ergebnis = zahl[2];
Den Wert eines Arrayelements kann man einer anderen Variablen zuweisen, die dann aber den gleichen Datentyp haben muss wie die Arrayelemente.
int summe = zahl[0] + zahl[1] + zahl[2];
Man kann mehrere Arrayelemente gleichzeitig auswerten.
if (zahl[1] < 0) zahl[1] = 0;
Man kann innerhalb von if-Anweisungen, while-Schleifen etc. auf den Wert bestimmter Arrayelemente zugreifen.
inventar[4].ausgeben();
In einem Objekt-Array kann auf die einzelnen Objekte zugegriffen werden, man kann sogar Methoden dieser Objekte aufrufen und ausführen lassen. Hier wird die ausgeben()-Methode des fünften Elementes des Arrays inventar aufgerufen. Das ist möglich, weil jedes Arrayelement ein eigenes Objekt mit Attributen und Methoden ist.
4.1.4 Übungen
Gegeben ist die Java-Klasse NZahlen (Natürliche Zahlen):
import java.util.Random;
public class NZahlen
{
int[] zahlen;
public NZahlen()
{
zahlen = new int[100];
}
public void ausgeben()
{
for (int i=0; i < 100; i++)
System.out.println("Zahl " + i + " = " + zahlen[i]);
}
public void mitZufallszahlenBelegen()
{
Random generator = new Random();
for (int i=0; i < 100; i++)
zahlen[i] = generator.nextInt(1000) + 1;
}
}
In der Methode mitZufallszahlenBelegen() wird zunächst ein Objekt generator der Klasse Random erzeugt.
In der for-Schleife wird dann jedem der 100 Arrayelement eine Zufallszahl im Bereich zwischen 1 und 1000 zugewiesen. Die Methode nextInt() des Objektes Random.generator liefert eine Zufallszahl zwischen 0 und 999, da noch 1 addiert wird, erhält man eine Zahl zwischen 1 und 1000.
Übung 4.1.3 #1
Schreiben Sie für die Klasse NZahlen eine sondierende Methode
public int gibSumme()
welche alle 100 Zufallszahlen des Arrays addiert und das Ergebnis dann zurückliefert.
Übung 4.1.3 #2
Schreiben Sie für die Klasse NZahlen eine sondierende Methode
public int gibKleinsteZahl()
welche alle 100 Zufallszahlen des Arrays durchsucht und als Ergebnis die kleinste Zahl zurückliefert.
Übung 4.1.3 #3
Schreiben Sie für die Klasse NZahlen eine sondierende Methode
public int gibAnzahlVon(int z)
welche als Ergebnis zurückliefert, wie oft die Zahl z in dem Array vorkommt.
Übung 4.1.3 #4
1.) Reduzieren Sie in Ihrem Quelltext die Zahl der Arrayelemente auf 20.
2.) Sorgen Sie dafür, dass die Arrayelemente nur mit Zufallszahlen zwischen 1 und 50 belegt werden.
3.) Schreiben Sie dann eine Methode
public void zeigeSterne()
welche die Werte der Arrayelemente als Reihe von Sternzeichen * in der Konsole anzeigt.
Angenommen, das erste Arrayelement enthält die Zahl 17, das zweite Element die Zahl 30 und das dritte Element die Zahl 12. Dann müssen die drei ersten Zeilen, die von dieser Methode auf die Konsole ausgegeben werden, so aussehen:
***************** ****************************** ************
Auf der Konsole sehen wir dann 20 Zeilen unterschiedlicher Länge an, da das Array jetzt aus 20 int-Zahlen besteht.
Übung 4.1.3 #5
Recherchieren Sie, was man unter einer for-each-Schleife versteht,
lassen Sie sich verschiedene Beispiele von einer KI erstellen und erklären
und fragen Sie die KI dann, was an dem folgenden Quelltext falsch ist:
public void ausgeben()
{
for (int i:zahlen)
System.out.println("Zahl " + i + " = " + zahlen[i]);
}
Wenn Sie selbst schon darauf gekommen sind, was hier falsch ist, entfällt natürlich die KI-Anfrage. Es sei denn, sie wollen mal überprüfen, wie gut die von Ihnen verwendete KI ist.
Versetzen Sie sich dann in die Lage, vor dem Kurs zu erklären, was hier falsch gemacht wurde.
Seitenanfang
Weiter mit Gegenständen, Rücksäcken und Helden ...