Kurzes Java-Programm, das sich komisch verhält

Diskutiere Kurzes Java-Programm, das sich komisch verhält im Java Basics - Anfänger-Themen Bereich.
X

Xulu200

Hallo!

Zuerst: Ich nutze IntelliJ IDEA 2020.1.1 Ultimate Edition.

Eigentlich wollte ich nur fix etwas schreiben, um damit ein Problem zu lösen. Bei dem Problem geht es um die Anzahl von Ziffernkombinationen unter bestimmten Eigenschaften. Die Details sind an der Stelle nicht relevant.

Bevor ich die Eigenschaften abfragen kann, benötige ich die n! Zahlenkombinationen, wenn ich alle n-stelligen Zahlen (jede Ziffer darf nur einmal vorkommen) als Listen innerhalb einer Menge haben möchte. Es muss nicht schön oder performant sein, sondern nur die Menge der Listen liefern.

Deshalb schrieb ich Folgendes:
Java:
import java.util.*;

public class main {
    public final static int c = 2;
    static HashSet<ArrayList<Integer>> set = new HashSet<ArrayList<Integer>>();

    public static void main(String args[]) {
        m(new ArrayList<>(), makeSet(c), c);
        System.out.println("SIZE:");
        System.out.println(set.size());
    }

    public static HashSet<Integer> makeSet(int maximum) {
        HashSet<Integer> s = new HashSet<Integer>();
        for (int i = 1; i <= maximum; i++) {
            s.add(i);
        }

        return s;
    }

    public static void m (ArrayList<Integer> list, HashSet<Integer> s, int len) {
        if (len == 0) {
            System.out.println("===\nlist bei len == 0: " + list);

            set.add(list);
            System.out.println("set: " + set + "\n===");
        }
        else {
            int counter = 1;

            for (int u : s) {
                ArrayList<Integer> l = list;
                System.out.println("l Anfang for: " + l);
                l.add(u);
                HashSet<Integer> q = new HashSet<Integer>();
                for (int z : s) {
                    if (z != u) {
                        q.add(z);
                    }
                }
                System.out.println("======\nLoop-Nr: " + counter);
                System.out.println("l = " + l);
                System.out.println("q = " + q);
                System.out.println("======\n");
                counter++;
                m(l, q, len-1);
            }
        }
    }
Da sind jetzt ein paar println-Ausgaben drin, weil ich dachte, das Problem könne ja nicht so lang sein und so fügte ich nach und nach mehr ein.

Nun, natürlich soll es am Ende um weit höhere Zahlen gehen, aber zum Erkennen des Problems sollte der Fall c = 2 reichen.
Ich erwarte also die Ausgabe, dass set am Ende [[1,2],[2,1]] enthält.

Die Ausgabe, bestehend aus den printlns, sieht aber so aus:

Code:
l Anfang for: []
======
Loop-Nr: 1
l = [1]
q = [2]
======

l Anfang for: [1]
======
Loop-Nr: 1
l = [1, 2]
q = []
======

===
list bei len == 0: [1, 2]
set: [[1, 2]]
===
l Anfang for: [1, 2]
======
Loop-Nr: 2
l = [1, 2, 2]
q = [1]
======

l Anfang for: [1, 2, 2]
======
Loop-Nr: 1
l = [1, 2, 2, 1]
q = []
======

===
list bei len == 0: [1, 2, 2, 1]
set: [[1, 2, 2, 1], [1, 2, 2, 1]]
===
SIZE:
2
Wie kann das sein, was sehe ich nicht? Danke sehr für die Hilfe! :)

Liebe Grüße
Xulu
 
L

LimDul

Erster Ratschlag. Bennene deine Variablen sinnvoll.

Java:
            for (int u : s) 
               for (int z : s)
Das sind keine sinnvollen Variablen. Wofür steht u, wofür z? s ist vermutlich das set.

Bei deiner Ausgabe gibt es l und q. Ich hab keine große Lust diese Verwirrung zu lösen und versuchen nachzuvollziehen was du dir dabei gedacht hast. Das einzige was ich auf die Schnelle sehe - du hast die beiden Schleifen und immer wenn es mindestens ein z gibt, was vom u abweicht. wird das z in q hinzugefügt. Wenn du die Liste 1,2 und enthält, dann ist das eigentlich sowohl für für 1 als auch 2 der Fall, weil für beide gibt es in dem Set ein Element das anders ist. Aber mir ist das zu komplex auseinderzudröseln was nun wieder q ist.
 
X

Xulu200

Okay, ich erkläre kurz:

1. Ich habe ein Set aus Listen (ja, es sind Permutationen), welches leer ist.
2. Ich rufe die rekursive Funktion m auf. Ihr übergebe ich eine neue leere Liste, ein Set aus den Zahlen von 1 bis c und das c.
3. m soll nun rekursiv arbeiten:
4. Eine Liste wird an m übergeben, ebenso eine Menge aus möglichen Ziffern, die an die Liste angehängt werden können.
5. Die Abbruchbedingung ist, dass die gewünschte Länge erreicht ist (also dann c).
6. Falls die Länge erreicht wurde, habe ich eine Permutation gefunden und kann diese in mein set aufnehmen.
7. Falls ich die Länge noch nicht erreicht habe, möchte ich mehrere rekursive Aufrufe starten. Für jedes mögliche Zeichen, das ich an die Liste anhängen kann, einen. Dabei machen s und z nichts Besonderes. Sie sorgen ausschließlich dafür, dass ein angehängtes Zeichen für den rekursiven Aufruf nicht mehr als anhängbar zur Verfügung steht.
Beispiel:
c = 8 und der Durchlauf steht bei [7,3,5,2]. m wird aufgerufen, indem [7,3,5,2] als bisheriges Ergebnis übergeben wird (Liste) und die Menge, die übergeben wird, ist [1,4,6,8], also die noch zu ergänzenden Zahlen. Die verbleibende Länge ist dann natürlich 4.

Tatsächlich gibt es fertige Lösungen und die Dokumentation ist auch nicht existent. Mit dem Programm möchte ich nach seinem Dienst nicht weiterarbeiten und kein anderer wird das Programm übernehmen. Da es nicht wesentlich länger wird und in seiner Kürze auch nicht sonderlich komplex, hatte ich das jetzt so gelassen.

Ich möchte trotz der Möglichkeit, das Programm neu schreiben zu können, verstehen, warum diese Implementierung nicht funktioniert.

Danke für eure Antworten! :)
Grüße
Xulu
 
X

Xulu200

Gelöst durch:

von
ArrayList<Integer> l = list;
zu
ArrayList<Integer> l = (ArrayList<Integer>) list.clone();

und von
m(l, q, len-1);
zu
m((ArrayList<Integer>) l.clone(), q, len-1);
 
J

JustNobody

In so einem kleinen Programm noch irrelevant aber in Projekten bezüglich Refactoring Möglichkeiten und so nicht sehr schön:
Statt einem clone Aufruf (der geht nur auf konkreten Klassen, die es implementieren, nicht auf Interfaces und man sollt ein der Regel gegen Interfaces implementieren!) solltest Du einfach eine neue ArrayList erzeugen. Ein Konstruktor nimmt auch eine Collection mit Elementen.

Also wäre der Code dann: ArrayList<Integer> l = new ArrayList<Integer>(list);

Das funktioniert auch, wenn list statt ArrayList nur List (also genaue Implementation nicht bekannt) oder LinkedList oder sonst irgendwas ist.

Die Problematik mit dem Refactoring von oben noch erläutert:
a) Du machst list aus irgend einem Grund zu einer LinkedList. Die Deklaration von list kann ja entfernt von der Deklaration von l sein. Du kannst es also übersehen. Und du bekommst es erst zur Laufzeit mit, weil der cast fehl schlägt.
b) Clean Code (z.B. Robert C Martin alias Uncle Bob in seinen Büchern) empfiehlt, generell gegen Interfaces zu programmieren wo möglich. Klar, bei der Erstellung einer Instanz geht das nicht - da muss irgendwo z.B. das new ArrayList<Integer>() kommen, aber bei allem, was Du weiter gibst oder so, sollte es dann das Interface sein. Und Du nutzt dann nur das Interface.
 
Thema: 

Kurzes Java-Programm, das sich komisch verhält

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben