Q1 OOP in Java

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.

Die Aufgabenstellung lautet

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]
Inhalt123456

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.java
/* meininfokurs.cmrg.site */
/**
 *
 * 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.

In der Aufgabenstellung steht die Signatur als 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]
Inhalt2337122277121

a) Welche Schleife durchläuft das ganze Array?

feld.length ist 6, gültige Indizes sind also 0 bis 5.

SchleifeWas passiert
i)i=0; i<feld.length; i=iEndlosschleife. 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-1Durchlä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.

WirkungArray 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:

  1. Spielfeld 10 × 10
  2. 5 Schiffe: eines mit Länge 5, eines mit 4, zwei mit 3, eines mit 2
  3. Schiffe liegen waagerecht oder senkrecht, nie diagonal, und überlappen sich nicht
  4. Ein Schuss trifft ein Feld; getroffen, versenkt oder daneben
  5. 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.

Spielfeld.java
/* meininfokurs.cmrg.site */
/**
 *
 * 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:

  1. i = 2 ist noch true → alle Vielfachen streichen: 4, 6, 8, 10, … 28
  2. i = 3 ist noch true → streichen: 6, 9, 12, 15, … 27
  3. i = 4 ist schon false (kam bei der 2 dran) → nichts tun
  4. i = 5 ist noch true → streichen: 10, 15, 20, 25
  5. … 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

Eratosthenes.java
/* meininfokurs.cmrg.site */
/**
 *
 * 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.

Aufgabe suchen

Tippe los. Strg+K öffnet die Suche von überall.

Zuletzt angesehen

Werbung