Wobei er ja einen Algorithmus hat. Nur eben hat er einen Fehler in seinem Algorithmus weil er halt eine Zählschleife missbraucht hat und so leider einen relativ schwer zu findenden Fehler eingebaut hat.
Ein Grund, wieso ich sowas nicht durchgehen lassen würde in einem Codereview. Man muss nur daran denken, dass dies auch ein schwer zu testender Fehler ist! So Dinge können durchaus mehrfach erfolgreich durchlaufen und dennoch ist es falsch. Mach da mal die Range größer - also Zahlen von 1-100 werden gezogen. Dann kann man da einen Test schreiben, der sich 6 so Zahlen geben lässt und prüft da dann auf Eindeutigkeit der Zahlen. Der Test wird mehrfach gut durchlaufen und hin und wieder auf einen Fehler stoßen. Aber das wird dann schon irgend ein Fehler des Build Systems gewesen sein ... ein neues mvn clean package lief ja erfolgreich (incl. Unit Tests) durch.
Daher: So Dinge immer gut unterteilen. Alleine schon, um wirklich ein guten Test für alle Teile haben zu können. Mit ist bewusst, dass ich hier jetzt über das Ziel hinaus schieße - es geht um eine Anfänger Aufgabe und da sind noch keine Unit Tests und co vorhanden. Aber dennoch macht es Sinn, hier einen Ausblick zu geben. Und es mag durchaus Andere geben, die über den Thread kommen, für die das Interessant ist.
Also betrachten wir die Aufgabe einfach einmal mit einem "tieferen Wissenstand".
Es sollen n unterschiedliche Zahlen erwürfelt werden mit einem Würfel mit n Seiten. (Ich habe mal statt 6 direkt ein n gesetzt) Und wir haben uns überlegt, dass wir das 1:1 umsetzen: KISS: Keep it simple, stupid. Also an der Stelle keine Optimierungen!
Wie gehen wir da vor? Wenn wir da keine Ahnung haben, dann probieren wir es aus. Egal wie: n = 2: Wir werfen eine Münze. n=6: Wir nehmen einfach einen Würfel. Wir nehmen einen Würfel und spielen das etwas durch und beobachten, was wir machen:
1. Wir würfeln
2. Wir schauen, ob die gewürfelte Zahl bereits auf dem Zettel steht. Ist das der Fall, dann starten wir beim ersten Schritt
3. Wir schreiben die gewürfelte Zahl auf den Zettel.
4. Wir prüfen, ob wir die gewünschte Anzahl an Zahlen haben. Wenn nicht, dann starten wir wieder beim ersten Schritt.
Wir verifizieren, ob das Funktioniert. Wir spielen es etwas durch und merken uns, was wir so brauchen. Alles, was wir brauchen, muss auf dem Zettel von uns stehen. Also haben wir auf dem Zettel: gewürfelte Zahl und die Liste an gewürfelten Zahlen. Evtl. auch schon die Anzahl der bereits notierten Zahlen (wobei wir die auch zählen könnten).
Wir merken aber beim durchspielen auch direkt: Das ist problematisch: Bei der 5ten und 6ten Zahl würfeln wir teilweise ganz schön lange. Evtl. überlegen wir uns also doch eine andere Lösung. Eine Idee könnte dann das "Kartenspiel" sein. Statt zu würfeln haben wir ein Kartenspiel mit n Karten, wir mischen es und haben dann die entsprechende Lösung. Sowas in der Art. Siehe Link aus #13 für diese Ideen. Darum geht es aber nicht. Wir sind bei der Umsetzung unserer Idee.
Und dann kommen wir jetzt dazu, den Algorithmus etwas aufzuschreiben.
Und das wird dann etwas wie:
Erwürfel die Zahlenreihe:
1. So lange noch nicht genug Zahlen gewürfelt wurden: Würfel die nächste eindeutige Zahl.
Würfel die nächste eindeutige Zahl
1. Würfel so lange eine neue Zahl, bis die Zahl eindeutig ist
2. gib die eindeutige Zahl zurück
Ist Zahl eindeutig?
1. Gehe alle bisher gewürfelten Zahlen durch:
1.1 Ist die aktuell gewürfelte Zahl == atuelle Zahl dann gib false zurück
2. gib true zurück
Würfel Zahl
1. Ermittel Zufallszahl von [1..n] und gib diese zurück
Somit haben wir schon 4 Algorithmen / Methoden. Das beschreibt das reine Vorgehen. Wir müssen jetzt nur überlegen, wie wir das mit den Daten modellieren. Da kann man dann schauen, was es so gibt. Wenn man nicht objektorientiert vorgehen will, dann hat man da z.B. einfach das Array mit int Werten, ein int wert, das angibt, bis wohin es gefüllt wurde und ein int wert mit der aktuell gewürfelten Zahl. Und man schaut dann, wo was benötigt wird und übergibt die Dinge einfach. Oder man macht es objektorientierter. Dann hat man z.B. eine Klasse Würfel. Man hat eine Klasse mit gewürfelten Zahlen. Man hat eine Klasse, die die Logik enthält. Wenn man sowas schaut, dann kann man überlegen, was es da jeweils an Daten und an Funktionalität gibt. Man kann überlegen, was es bereits gibt. Und dann stellt man fest, dass man ggf. manche Klasse gar nicht zwingend braucht:
a. Würfel ist evtl. einfach die Klasse Random. Wobei das schon lesbarer sein kann, da eine eigene Klasse zu haben.
b. Die Liste gewürfelter Zahlen kann evtl. einfach eine List oder hier speziell Set sein. Wenn es ein Set ist, dann wird plötzlich vieles einfacher. Da ein Set keine doppelten Zahlen haben kann, wird aus dem Algorithmus einfach ein
"Füge dem Set gewürfelter Zahlen so lange die nächste gewürfelte Zahl hinzu, bis das Set n Elemente hat."
Auf jeden Fall kann man dann sowas unterteilen. Man erhält dann mehrere keine Methoden. Jede Methode für sich ist gut überschaubar. Und dann kann man jede Methode auch einzeln testen.
Das nur einmal als sehr ausführliche Beschreibung zu dem, was bereits geschrieben wurde. Vielleicht hilft das ja dem Einen oder Anderen.