Arrays
Lösung geprüft
Die Aufgabe der heutigen Stunde sind die Arrays! Vier Teilaufgaben, und die haben es teilweise in sich – vor allem Aufgabe 2, wo du Schleifen im Kopf durchgehen musst, ohne sie laufen zu lassen. Genau so etwas kommt in der Klausur dran, weil man da keinen Rechner hat.
Aufgabe 1: Gegeben ist ein Programmcode, der ein
Array wuerfelergebnisse anlegt. a) Skizzieren Sie das
Array. b) Geben Sie Deklaration und Initialisierung mithilfe einer
Zahlenliste in geschweiften Klammern an. c) Entwickeln Sie ein
Programm, das dasselbe Array mithilfe einer for-Schleife
anlegt. d) Implementieren Sie eine Würfelsimulation mit 30 Würfen in
einem Array zufallsergebnisse. e) Geben Sie die
Ergebnisse in der Form „Wurf 1 ergab 5." aus. Außerdem: Analysieren
Sie die Methode wasMacheIch(pWert : int) und geben Sie
eine vollständige Methodendokumentation an.
Aufgabe 2: Gegeben ist das Array feld. a) Geben Sie an, bei welcher der Zählschleifen i) bis v) das gesamte Array durchlaufen wird. b) Beschreiben Sie die Wirkung der folgenden Anweisungen auf das Array. Wird der Inhalt verändert, so geben Sie das veränderte Array an.
Aufgabe 3: Schiffe versenken. Einigen Sie sich auf ein Regelwerk, entwickeln Sie ein Entwurfs- und ein Implementierungsdiagramm und implementieren Sie das Spiel.
Aufgabe 4: Stellen Sie die Funktionsweise des
Algorithmus „eratosthenes" mit max = 30 dar und
implementieren Sie ihn in Java.
Aufgabe 1 – Würfelergebnisse
Gegeben ist dieser Code:
int [] wuerfelergebnisse = new int[6];
wuerfelergebnisse[2] = 3;
wuerfelergebnisse[0] = 1;
wuerfelergebnisse[5] = 6;
wuerfelergebnisse[1] = 2;
wuerfelergebnisse[4] = 5;
wuerfelergebnisse[3] = 4;
a) Die Skizze
Die Zuweisungen stehen kreuz und quer, das ist Absicht – man soll merken, dass die Reihenfolge der Zeilen nichts mit der Reihenfolge im Array zu tun hat. Entscheidend ist der Index in den eckigen Klammern. Sortiert man das, kommt das hier raus:
| Index | [0] | [1] | [2] | [3] | [4] | [5] |
|---|---|---|---|---|---|---|
| Inhalt | 1 | 2 | 3 | 4 | 5 | 6 |
Der Index fängt bei 0 an und hört bei 5 auf. Ein Array der Länge 6 hat
also keinen Index 6. Das ist der Klassiker, an dem man
sich eine ArrayIndexOutOfBoundsException einfängt.
b) Mit Zahlenliste
int[] wuerfelergebnisse = {1, 2, 3, 4, 5, 6};
Das new int[6] braucht man hier nicht mehr – Java zählt
die Werte in den geschweiften Klammern selbst und macht das Array genau
so groß. Das geht aber nur direkt bei der Deklaration. Später
wuerfelergebnisse = {1,2,3}; zu schreiben ist ein
Syntaxfehler.
c) Mit einer for-Schleife
int[] wuerfelergebnisse = new int[6];
for (int i = 0; i < wuerfelergebnisse.length; i++) {
wuerfelergebnisse[i] = i + 1;
}
Der Zusammenhang ist Wert = Index + 1. Schreib
wuerfelergebnisse.length statt einer 6 – wenn du das Array
später größer machst, läuft die Schleife automatisch mit.
d) und e) Die Würfelsimulation
/**
*
* Wuerfelsimulation mit 30 Wuerfen
*
* @version 1.0 vom 14.09.2025
* @meininfokurs.cmrg.site
*/
public class Wuerfelsimulation {
private int[] zufallsergebnisse = new int[30];
public void wuerfeln() {
for (int i = 0; i < zufallsergebnisse.length; i++) {
zufallsergebnisse[i] = (int)(Math.random() * 6 + 1);
}
}
public void ausgabe() {
for (int i = 0; i < zufallsergebnisse.length; i++) {
System.out.println("Wurf " + (i + 1) + " ergab " + zufallsergebnisse[i] + ".");
}
}
/**
* Berechnet den Mittelwert der ersten pWert Wuerfe.
*
* @param pWert Anzahl der zu beruecksichtigenden Wuerfe,
* sinnvoll ist 1 bis zufallsergebnisse.length
* @return Durchschnitt der ersten pWert Wuerfe als Dezimalzahl
*/
public double wasMacheIch(int pWert) {
double ergebnis = 0;
for (int i = 0; i < pWert; i++) {
ergebnis += zufallsergebnisse[i];
}
return ergebnis / pWert;
}
public static void main(String[] args) {
Wuerfelsimulation w = new Wuerfelsimulation();
w.wuerfeln();
w.ausgabe();
System.out.println("");
System.out.println("Durchschnitt aller 30 Wuerfe: " + InOut.format2(w.wasMacheIch(30)));
} // end of main
} // end of class Wuerfelsimulation
Zu (int)(Math.random()*6 + 1):
Math.random() liefert eine Kommazahl von 0 bis knapp unter
1. Mal 6 macht daraus 0 bis knapp unter 6, plus 1 ergibt 1 bis knapp
unter 7. Das (int) davor schneidet die Nachkommastellen ab
– nicht runden, abschneiden. Übrig bleiben die ganzen
Zahlen 1 bis 6.
Bei der Ausgabe steht (i + 1) in Klammern. Das ist
wichtig: ohne Klammern würde Java "Wurf " + i als Text
zusammenbauen und danach + 1 einfach anhängen. Aus Wurf 0
würde dann „Wurf 01". Sobald links ein String steht, ist das
+ kein Plus mehr.
Was macht wasMacheIch?
Die Methode summiert die ersten pWert Einträge des Arrays
auf und teilt am Ende durch pWert. Sie berechnet also den
Mittelwert der ersten pWert Würfe. Bei
wasMacheIch(30) ist das der Durchschnitt aller Würfe – der
sollte bei einem fairen Würfel in der Nähe von 3,5 liegen.
wasMacheIch(pWert : int) : int, im abgedruckten Code steht
aber public double wasMacheIch(int pWert). Das passt nicht
zusammen. Richtig ist double – ein Mittelwert ist nun mal
selten eine ganze Zahl. Mit int als Rückgabetyp würde aus
3,7 eine 3.
Zwei Sachen fallen beim Analysieren noch auf: Wird
pWert mit 0 aufgerufen, wird durch 0 geteilt und es kommt
NaN raus. Und ist pWert größer als das Array,
fliegt eine ArrayIndexOutOfBoundsException. Beides steht
deshalb als Einschränkung in der Dokumentation.
Aufgabe 2 – Schleifen im Kopf durchgehen
Gegeben ist feld mit sechs Werten:
| Index | [0] | [1] | [2] | [3] | [4] | [5] |
|---|---|---|---|---|---|---|
| Inhalt | 23 | 37 | 12 | 22 | 77 | 121 |
a) Welche Schleife durchläuft das ganze Array?
feld.length ist 6, gültige Indizes sind also 0 bis 5.
| Schleife | Was passiert | |
|---|---|---|
| i) | i=0; i<feld.length; i=i | Endlosschleife. i=i verändert nichts, i bleibt für immer 0. |
| ii) | i=1; i>feld.length; i++ | Läuft kein einziges Mal. 1 > 6 ist gleich zu Beginn falsch. |
| iii) | i=0; i<feld.length; i++ | Durchläuft alles. 0 bis 5, vorwärts. |
| iv) | i=1; i<=feld.length-1; i++ | Läuft 1 bis 5 – feld[0] wird ausgelassen. |
| v) | i=feld.length-1; i>=0; i=i-1 | Durchläuft alles. 5 bis 0, rückwärts. |
Die Antwort ist also: iii) und v). Bei v) lohnt es sich hinzusehen – die läuft rückwärts, erwischt aber trotzdem jeden Index genau einmal. „Vollständig durchlaufen" heißt nicht „von vorne nach hinten".
iv) ist die fieseste. i<=feld.length-1 ist als obere
Grenze völlig korrekt, dieselbe wie bei iii). Der Fehler steckt im
Start: i=1 statt i=0. Solche Off-by-one-Fehler
übersieht man beim schnellen Lesen fast immer.
b) Wirkung der Anweisungen
Jede Anweisung wird einzeln auf das ursprüngliche Array angewendet.
| Wirkung | Array danach | |
|---|---|---|
| i) | feld[4] wird auf 23 gesetzt und sofort wieder auf 77. Die erste Zuweisung ist damit umsonst. | unverändert 23, 37, 12, 22, 77, 121 |
| ii) | Gibt den Wert 12 zurück und beendet die Methode. Lesen verändert nichts. | unverändert 23, 37, 12, 22, 77, 121 |
| iii) | Setzt die Indizes 0, 1 und 2 auf 2. | 2, 2, 2, 22, 77, 121 |
| iv) | Setzt Index 2 und 3 auf den Wert von feld[0], also 23. | 23, 37, 23, 23, 77, 121 |
| v) | i startet bei 1 und wird hochgezählt, die Bedingung i>0 bleibt wahr. Praktisch eine Endlosschleife. feld[2] steht auf 15. | 23, 37, 15, 22, 77, 121 – das Programm hängt aber |
Zu v) noch ein Nachtrag für die Genauen: streng genommen ist das keine
echte Endlosschleife. i läuft irgendwann über den größten
int hinaus und wird negativ, dann ist i>0
falsch und die Schleife endet. Das dauert gut zwei Milliarden
Durchläufe. Praktisch sitzt du da und wartest.
Aufgabe 3 – Schiffe versenken
Hier legt die Aufgabe das Regelwerk nicht fest, das sollt ihr im Kurs selbst festlegen. Ich nehme die einfachste Variante, die noch spielbar ist:
- Spielfeld 10 × 10
- 5 Schiffe: eines mit Länge 5, eines mit 4, zwei mit 3, eines mit 2
- Schiffe liegen waagerecht oder senkrecht, nie diagonal, und überlappen sich nicht
- Ein Schuss trifft ein Feld; getroffen, versenkt oder daneben
- Das Spiel ist vorbei, wenn alle Schiffsfelder getroffen sind
Beim Entwurfsdiagramm reicht eine Klasse Spielfeld. Die
entscheidende Frage der Aufgabe ist die Datenstruktur – und das ist ein
zweidimensionales Array:
private char[][] feld = new char[10][10];
Das liest sich als „ein Array aus 10 Arrays mit je 10 Zeichen", also
Zeilen und Spalten. feld[3][7] ist Zeile 3, Spalte 7.
char bietet sich an, weil wir pro Feld nur einen Zustand
brauchen und den beim Ausgeben direkt hinschreiben können.
/**
*
* Schiffe versenken - Spielfeld mit zweidimensionalem Array
*
* @version 1.0 vom 14.09.2025
* @meininfokurs.cmrg.site
*/
public class Spielfeld {
// Zustaende eines Feldes
// '~' Wasser, 'S' Schiff, 'X' Treffer, 'o' Fehlschuss
private char[][] feld = new char[10][10];
private int[] laengen = {5, 4, 3, 3, 2};
private int schiffsfelder = 0;
private int treffer = 0;
public Spielfeld() {
for (int zeile = 0; zeile < feld.length; zeile++) {
for (int spalte = 0; spalte < feld[zeile].length; spalte++) {
feld[zeile][spalte] = '~';
}
}
schiffeSetzen();
}
private void schiffeSetzen() {
for (int i = 0; i < laengen.length; i++) {
boolean gesetzt = false;
while (!gesetzt) {
boolean waagerecht = Math.random() < 0.5;
int zeile = (int)(Math.random() * 10);
int spalte = (int)(Math.random() * 10);
if (passt(zeile, spalte, laengen[i], waagerecht)) {
for (int k = 0; k < laengen[i]; k++) {
if (waagerecht) {
feld[zeile][spalte + k] = 'S';
} else {
feld[zeile + k][spalte] = 'S';
}
}
schiffsfelder += laengen[i];
gesetzt = true;
}
}
}
}
private boolean passt(int zeile, int spalte, int laenge, boolean waagerecht) {
// ragt das Schiff ueber den Rand hinaus?
if (waagerecht && spalte + laenge > 10) {
return false;
}
if (!waagerecht && zeile + laenge > 10) {
return false;
}
// liegt da schon ein Schiff?
for (int k = 0; k < laenge; k++) {
if (waagerecht && feld[zeile][spalte + k] == 'S') {
return false;
}
if (!waagerecht && feld[zeile + k][spalte] == 'S') {
return false;
}
}
return true;
}
public void schiessen(int zeile, int spalte) {
if (zeile < 0 || zeile > 9 || spalte < 0 || spalte > 9) {
System.out.println("Das Feld gibt es nicht.");
return;
}
if (feld[zeile][spalte] == 'S') {
feld[zeile][spalte] = 'X';
treffer++;
System.out.println("Treffer!");
} else if (feld[zeile][spalte] == '~') {
feld[zeile][spalte] = 'o';
System.out.println("Daneben.");
} else {
System.out.println("Da hast du schon hingeschossen.");
}
}
public boolean istFertig() {
return treffer == schiffsfelder;
}
public void anzeigen() {
String ausgabe = " 0 1 2 3 4 5 6 7 8 9\n";
for (int zeile = 0; zeile < feld.length; zeile++) {
ausgabe += zeile + " ";
for (int spalte = 0; spalte < feld[zeile].length; spalte++) {
// Schiffe bleiben verdeckt, bis sie getroffen werden
char zeichen = feld[zeile][spalte];
if (zeichen == 'S') {
zeichen = '~';
}
ausgabe += " " + zeichen;
}
ausgabe += "\n";
}
System.out.println(ausgabe);
}
public static void main(String[] args) {
Spielfeld spiel = new Spielfeld();
int schuesse = 0;
while (!spiel.istFertig()) {
spiel.anzeigen();
int zeile = InOut.readInt("Zeile: ");
int spalte = InOut.readInt("Spalte: ");
spiel.schiessen(zeile, spalte);
schuesse++;
}
System.out.println("Alle Schiffe versenkt - mit " + schuesse + " Schuessen.");
} // end of main
} // end of class Spielfeld
Zwei Stellen sind erklärungsbedürftig. Die while-Schleife
in schiffeSetzen würfelt so lange eine Position aus, bis
eine passt. Das klingt nach einer schlechten Idee, funktioniert bei fünf
Schiffen auf 100 Feldern aber problemlos – es ist genug Platz da.
Und die innere Schleife in anzeigen läuft über
feld[zeile].length, nicht über feld.length.
Bei einem quadratischen Feld ist das dasselbe, bei einem 10×20-Feld
nicht mehr. feld.length ist die Anzahl der
Zeilen, feld[zeile].length die Länge einer
einzelnen Zeile.
Aufgabe 4 – Das Sieb des Eratosthenes
Diese Aufgabe ist mit einem Stern markiert, also freiwillig. Sie lohnt
sich trotzdem, weil sie zeigt, wozu ein boolean-Array gut
ist.
a) Funktionsweise mit max = 30
Die Idee: man streicht der Reihe nach alle Vielfachen weg. Was übrig
bleibt, sind die Primzahlen. Am Anfang steht in
istPrimzahl überall true:
i = 2ist nochtrue→ alle Vielfachen streichen: 4, 6, 8, 10, … 28i = 3ist nochtrue→ streichen: 6, 9, 12, 15, … 27i = 4ist schonfalse(kam bei der 2 dran) → nichts tuni = 5ist nochtrue→ streichen: 10, 15, 20, 25- … und so weiter bis 29
Übrig bleiben die Indizes, bei denen noch true steht und
die größer als 1 sind:
primzahlen = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Beachte die Bedingung istPrimzahl[i] && i>1 im
Struktogramm. Bei 0 und 1 steht noch true drin, weil sie
nie als Vielfaches gestrichen wurden – Primzahlen sind sie trotzdem
nicht. Deshalb das i>1.
b) Der Algorithmus in Java
/**
*
* Das Sieb des Eratosthenes, 1:1 nach dem Struktogramm
*
* @version 1.0 vom 14.09.2025
* @meininfokurs.cmrg.site
*/
public class Eratosthenes {
public int[] eratosthenes(int max) {
boolean[] istPrimzahl = new boolean[max];
int anzahl = 0;
int index = 0;
// alle Felder auf true
for (int i = 0; i < max; i++) {
istPrimzahl[i] = true;
}
// Vielfache streichen
for (int i = 2; i < max; i++) {
if (istPrimzahl[i]) {
for (int v = i * 2; v < max; v += i) {
istPrimzahl[v] = false;
}
}
}
// zaehlen, wie viele uebrig sind
for (int i = 0; i < max; i++) {
if (istPrimzahl[i] && i > 1) {
anzahl++;
}
}
// die uebrigen einsammeln
int[] primzahlen = new int[anzahl];
for (int i = 0; i < max; i++) {
if (istPrimzahl[i] && i > 1) {
primzahlen[index] = i;
index++;
}
}
return primzahlen;
}
public static void main(String[] args) {
Eratosthenes sieb = new Eratosthenes();
int[] ergebnis = sieb.eratosthenes(30);
String ausgabe = "Primzahlen bis 30:";
for (int i = 0; i < ergebnis.length; i++) {
ausgabe += " " + ergebnis[i];
}
System.out.println(ausgabe);
} // end of main
} // end of class Eratosthenes
Warum wird zweimal über das Array gelaufen – einmal zum Zählen, einmal
zum Einsammeln? Weil ein Array in Java seine Größe beim Anlegen braucht
und sie danach nicht mehr ändern kann. Wir müssen also erst wissen,
wie viele Primzahlen es sind, bevor wir
new int[anzahl] schreiben können. Genau so steht es auch im
Struktogramm.
Und genau das ist der Grund, warum es die ArrayList gibt –
die wächst von selbst mit. Die schauen wir uns als Nächstes an.