Alle möglichen Kombinationen aus einem Zeichensatz - Verbesserungsvorschläge?

Kai008

Erfahrenes Mitglied
Ja, ich bins mal wieder mit der bitte um Verbesserungsvorschläge. Ich wollte aus einem Zeichensatz alle Möglichen Kombinationen innerhalb einer bestimmten Länge generieren. Habe ich in einer Schleife auch geschafft. Allerdings erscheint mir dieser Weg sehr umständlich, deshalb wollte ich fragen, ob jemand einen anderen besseren Weg kennt, damit ich auch dazulerne, und nicht immer die selben schlechten Sources schreibe. =)
Alle paar mal (Im mom. jedes Millionte mal) wird der erste und der letzte String in einer Klasse zusammengefasst.

Java:
package core;

public class Jobsheduler extends AbstractJobSheduler {
	private Command command;
	
	Jobsheduler(Command command) {
		super();
		this.command = command;
	}
	public void run() {
		try {
			String hash = command.getParameter(0);
			String charset = command.getParameter(1);
			int minTestLenght = Integer.parseInt(command.getParameter(2));
			int maxTestLenght = Integer.parseInt(command.getParameter(3));

			char[] charsetChars = charset.toCharArray();
			char[] lastHashChars = new char[maxTestLenght];
			for(byte b = 0; b < maxTestLenght; b++)
				lastHashChars[b] = charsetChars[charsetChars.length - 1];
			
			boolean loopRun = true;
			char[] plainChars = new char[minTestLenght];
			command = new Command(null, null);
			command.addParameter("md5.jar");
			command.addParameter(hash);
			command.addParameter(charset);
			for(long loopCounter = 0L; loopRun; loopCounter++) {
				plainChars = add(plainChars.length - 1, charsetChars,plainChars, maxTestLenght);
				
				if(String.valueOf(plainChars).equals(String.valueOf(lastHashChars))) {
					if(loopCounter == 0) {
						command.addParameter(String.valueOf(plainChars));						
					}
					command.addParameter(String.valueOf(plainChars));
					loopRun = false;
				} else if(loopCounter == 0) {
					command.addParameter(String.valueOf(plainChars));
				} else if(loopCounter == Math.pow(10.0d, 6.0d)) {
					command.addParameter(String.valueOf(plainChars));
					command = new Command(null, null);
					command.addParameter("md5.jar");
					command.addParameter(hash);
					command.addParameter(charset);
					loopCounter = -1;
				}
			}
		}
		catch (NumberFormatException e) {}
	}
	private char[] add(int location, char[] charsetChars, char[] plainChars, int maxTestLenght)
	{
		int positionFromCharAtLocationInCharset = getPostitionFromCharInCharset(location, charsetChars, plainChars);

		if(positionFromCharAtLocationInCharset == charsetChars.length - 1 && location != 0) {
			plainChars = add(location - 1, charsetChars, plainChars, maxTestLenght);
			plainChars[location] = charsetChars[0];
		} else if(positionFromCharAtLocationInCharset == charsetChars.length - 1 &&
				plainChars.length != maxTestLenght) {
			plainChars = copyArray(charsetChars[0], plainChars);
		} else {
			plainChars[location] = charsetChars[positionFromCharAtLocationInCharset + 1];
		}
		return(plainChars);
	}
	private char[] copyArray(char firstCharInCharset, char[] plainChars)
	{
		char[] newArray = new char[plainChars.length + 1];
		for(byte b = 0; b < newArray.length; b++) {
			newArray[b] = firstCharInCharset;
		}
		return(newArray);
	}
	private int getPostitionFromCharInCharset(int location, char[] charsetChars, char[] plainChars)
	{
		char plainChar = plainChars[location];
		int positionFromCharAtLocationInCharset = -1;
		for(byte b = 0; b < charsetChars.length && positionFromCharAtLocationInCharset == -1; b++) {
			if(charsetChars[b] == plainChar) {
				positionFromCharAtLocationInCharset = b;				
			}
		}
		return(positionFromCharAtLocationInCharset);
	}
}
 
Ich habe mir Hilfsklassen geschrieben, mit denen ich das Problem iterativ lösen kann.
Hier hast du mein Testprogramm, das ich bei der Entwicklung geschrieben habe.

Die Hauptklasse mit der main()-Funktion: sie erzeugt die GUI und verabeitet die Eingabe. Das Ergebnis wird in einem JTextArea ausgegeben.
Java:
import java.awt.BorderLayout;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;


public class Combine 
extends JFrame
implements ActionListener
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new Combine();

	}
	JButton btnRun = new JButton("Los");
	JTextField jtf = new JTextField();
	JTextArea jta = new JTextArea();
	JScrollPane jsp;
	JPanel pnl = new JPanel();
	public Combine()
	{
		this.setLayout(new BorderLayout());
		jsp = new JScrollPane(jta);
		add(jsp,BorderLayout.CENTER);
		add(jtf,BorderLayout.NORTH);
		add(btnRun,BorderLayout.EAST);
		btnRun.setActionCommand("Run");
		btnRun.addActionListener(this);
		
		jtf.setText("1234");
		setInitialSize();
		setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 
		setVisible(true);
	}
	// positioniert das Fenster in der Mitte des Bildschirms
	// Breite/Höhe des Fensters wird auf halbe Bildschirm-Breite/Höhe gesetzt 
	private void setInitialSize()
	{
		GraphicsEnvironment ge 
		= GraphicsEnvironment.getLocalGraphicsEnvironment();
		Rectangle rct = ge.getMaximumWindowBounds();
		Rectangle bnd = new Rectangle();
		bnd.width = rct.width/2;
		bnd.height = rct.height/2;
		bnd.x = rct.x + bnd.width/2;
		bnd.y = rct.y + bnd.height/2;
		setBounds(bnd);
	}
	// Beim Button-Click die Verarbeitungs-Funktion starten
    public void actionPerformed( ActionEvent e ) { 
        if ( e.getActionCommand().equals("Run") )
        { Run();
        }
    } 
    public void Run() 
    { 
    	String str = new String(jtf.getText());
    	int len = str.length();
    	int idx;
    	String[] src;
    	String dst = "";
    	// Schleife über die Länge des zu permutierenden 'Alphabets'
    	for ( int i = 1; i <= len; i++ )
    	{
    		// vrd verteilt die Menge der zu überspringenden Zeichen
    		// auf die Zwischenräume
    		vrDistributor vrd = new vrDistributor(len-i,i+1);
    		// für jede Möglichkeit...
    		for ( int[] arr1 : vrd )
    		{
    			idx = 0; // die Position des zu entnehmenden Zeichens
    			src = new String[i];
    			int cnt = 0;
    			// die Zeichen des Alphabets werden heruasgepickt
    			// in arr1 ist gespeichert, wie viele Zeichen übersprungen werden sollen 
    			for ( int j : arr1 )
    			{
    				// den aktuellen Index berechnen
    				idx += j;
    				// nur so viele entnehmen, wie gewünscht sind
    				if ( cnt >= i ) break;
    				// das ermittelte Zeichen entlesen
    				src[cnt] = str.substring(idx, idx+1);
    				// Index für den nächsten Durchlauf aktualisieren
    				// die Länge des herauskopierten Strings muss draufaddiert werden
    				idx++;
    				// mitzählen, wie viele Zeichen gelesen wurden
    				cnt++;
    			}
    			// Nun haben wir in src ein Array über die zu permutierenden Strings
    			// mit Hilfe von vrPermutator lassen wir uns das Array src
    			// jedesmal zu einer neuen Permutation umgruppieren
    			do
    			{
    				// die Permutation der Strings wird zu einer Ergebniszeile zusammengesetzt
    				// dst ist der Text, der später angezeigt werden soll
    				for ( String tmp : src ) dst += tmp; 
    				// Zeilenumbruch nicht vergessen
    				dst += "\n";
    			}
    			while ( vrPermutator.createNextPermutation(src) );
    			// nun haben wir für das aktuelle 'Alphabet' alle Permutationen erzeugt
    		}
    		// nun haben wir alle 'Alphabete' der momentan gewünschten Länge verarbeitet
    	}
    	// jetzt haben wir alle Alphabete jeder möglichen Länge  durchpermutiert
    	// Ausgabe des erzeugten Textes
    	jta.setText(dst);
    } 
}

vrDistributor wird verwendet, um die jeweils zu überspringenden Zeichen zu ermitteln.
Java:
import java.util.Iterator;
import java.util.NoSuchElementException;

// vrDistributor liefert sukzessive alle Möglichkeiten,
// n Elemente auf k Boxen zu verteilen
// - n darf nicht negativ sein
// - k darf nicht negativ sein, darf aber sowohl größer
//     als auch kleiner (oder gleich) n sein
// Die statischen Methoden verändern das übergebene Array
// Die Objektmethoden arbeiten auf dem intern angelegten Array
// und liefern eine Kopie des Ergebnisses zurück
// Die Klasse implementiert das Interface Iterable,
// um ein Durchlaufen mit Hilfe des erweiterten for zu ermöglichen.
// Es existiert ein Kopier-Konstruktor
// Die überschriebene clone()-Methode nutzt ihn, um eine Kopie von sich selber zu erzeugen 
public class vrDistributor
implements Iterable<int[]>, Cloneable
{
	public static class NegativeNumberException 
	extends RuntimeException
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		public NegativeNumberException()
		{}
	}

	private int count;
	private int boxes;
	private int arr[];
	
	public vrDistributor()
	{ init(0,0); }
	public vrDistributor(vrDistributor d)
	{
		init( d.count, d.boxes );
		if ( d.arr == null ) return;
		arr = d.arr.clone();
	}
	public vrDistributor(int elements, int containers)
	throws NegativeNumberException
	{ init(elements,containers); }
	private void init(int elements, int containers)
	throws NegativeNumberException
	{
		if ( elements < 0 || containers < 0 )
		{ throw new NegativeNumberException(); }
		count = elements;
		boxes = containers;
		clear();
	}

	public int getElements()
	{ return count; }
	public int setElements(int elements)
	throws NegativeNumberException
	{ 
		if ( elements < 0 )
		{ throw new NegativeNumberException(); }
		count = elements;
		clear();
		return count;
	}

	public int getContainers()
	{ return boxes; }
	public int setContainers(int containers)
	throws NegativeNumberException
	{ 
		if ( containers < 0 )
		{ throw new NegativeNumberException(); }
		boxes = containers;
		clear();
		return boxes;
	}
	
	// erzeugt die erste mögleiche Verteilung
	private void reset()
	{
		arr = new int[boxes];
		arr[boxes-1] = count;
	}
	// löscht das interne Array, um anzuzeigen,
	// dass noch keine Verteilung erzeugt wurde
	public  void clear()
	{ arr = null; }
	
	public boolean hasNext()
	{
		if ( arr == null 
		&&	 boxes >  0
		&&   count >= 0
		){ return true; }
		if ( arr != null
		&&   boxes >  0
		&&   count >= 0
		&&   arr[0] != count
		){ return true; }
		return false;
	}
	
	private static int[] prvRearrange(int[] arr, boolean cyclic, int count)
	{
		int boxes = arr.length;
        // skip leading zeroes
		int i;
		for ( i = 0; i < boxes; i++ )
		{ if ( arr[i] != 0 ) break; }
        // actual container is not the first
        // -> put one element from here to the previous container, return
        if ( i > 0 )
        { arr[i]--; arr[i - 1]++; return arr; }
        // if no further distribution is possible
        // -> return null or the first possible distribution
        if ( arr[0] == count ) 
        { 
        	if ( cyclic ) { arr[boxes-1] = count; arr[0] = 0; return arr; }
        	else { return null; }
        }
        // skip empty containers
        int j;
        for (j = i+1; j < boxes; j++)
        { if (arr[j] != 0) break; }
        // remove one element  from the j-container
        arr[j]--;
        // remove all elements from the i-container
        int val = arr[i]; arr[i] = 0;
        // and put them to the (j-1)-container
        // together with the one from the j-container
        arr[j-1] = val+1;
        // return result;
        return arr;
	}
	public  static int[] rearrange(int[] arr)
	{ return rearrange(arr,false); }
	public  static int[] rearrange(int[] arr, boolean cyclic)
	{
		// parameter test 
		if ( arr == null )
		{ return arr; }
		// count sum of numbers
		// test for integrity
		int count = 0;
		for ( int val : arr )
		{
			if ( val < 0 )
			{ throw new NegativeNumberException(); }
			count += val;
		}
        // calculate and return result;
        return prvRearrange(arr,cyclic,count);
	}
	
	public int[] next()
	{ return next(false); }
	public int[] next(boolean cyclic)
	{
		// if the first distribution is needed, create it and return 
		if ( arr == null )
		{ reset(); return arr.clone(); }
		if ( arr[0] == count )
		{ 
			if ( !cyclic ) 
			{ return arr=null; } 
			reset(); 
			return arr.clone(); 
		}
		arr = prvRearrange(arr, cyclic, count);
		return (arr==null) ? null : arr.clone();
	}
	
	public Iterator<int[]> iterator()
	{
		return new Iterator<int[]>()
		{
			public boolean hasNext()
			{ return vrDistributor.this.hasNext(); }
			public int[] next()
			{ 
				int[] ret = vrDistributor.this.next();
				if ( ret == null )
				{ throw new NoSuchElementException(); }
				return ret;
			}
			public void remove()
			{ throw new UnsupportedOperationException(); }
		};
	}
	
    @Override
	public Object clone()
	{ return new vrDistributor(this); }
    @Override
	public boolean equals (Object o)
	{
		if ( o == null ) return false;
		vrDistributor d = (vrDistributor)o;
		if ( d.count != count ) return false;
		if ( d.boxes != boxes ) return false;
		if ( d.arr == null && arr != null ) return false;
		if ( d.arr != null && arr == null ) return false;
		if ( d.arr.equals(arr) == false ) return false;
		return true;
	}
}

vrPermutator liefert bei jedem Aufruf die nächste Permutation des übergebenen Arrays.
Java:
public class vrPermutator {
	/*
	The following algorithm generates the next permutation lexicographically after a given permutation. 
	It changes the given permutation in-place.
	It is known as SEPA, and described at http://www.freewebs.com/permute/soda_submit.html, for example.
	At this web site there is given an example implementation in C, too. 

	   1. Find the largest index j such that a[j] < a[j + 1]. If no such index exists, the permutation is the last permutation.
	   2. Find the largest index l such that a[j] < a[l]. Such a l exists and satisfies j < l, since j + 1 is such an index.
	   3. Swap a[j] with a[l].
	   4. Reverse the sequence from a[j + 1] up to an including the final element a[n].

	After step 1, one knows that all of the elements strictly after position j 
	form a weakly decreasing sequence, so no permutation of these elements will make it advance
	in lexicographic order; to advance one must increase a[j]. 
	Step 2 finds the smallest value a[l] to replace a[j] by, 
	and swapping them in step 3 leaves the sequence after position j in weakly decreasing order. 
	Reversing this sequence in step 4 then produces its lexicographically minimal permutation, 
	and the lexicographic successor of the initial state for the whole sequence.
	*/
	@SuppressWarnings("unchecked")
	public static boolean createNextPermutation(Comparable[] arr)
	{
		if ( arr == null ) return false;
		int j, l;
		Comparable v;
		int len = arr.length;
		if ( len < 2 ) return false;
		int max = len-1;
		// step 1
		for ( j = max; j > 0; j-- )
		{ if ( arr[j-1].compareTo(arr[j]) < 0 ) break; }
		if ( j-- == 0 ) return false;
		// step 2
		v = arr[j];
		for ( l = max; l > j; l-- )
		{ if ( v.compareTo(arr[l]) < 0 ) break; }
		// step 3
		arr[j] = arr[l];
		arr[l] = v;
		// step 4
		for ( j++, l = max; l > j; l--, j++ )
		{ v = arr[l]; arr[l] = arr[j]; arr[j] = v; }
		return true;
	}
}

Ich hoffe, meine Kommentare sind einleuchtend genug. Für Rückfragen stehe ich gerne zur Verfügung.
 
Danke, aber meinst du, dass diese Klassen wirklich besser sind als meine?
Schaut zumindest für mich um ein vielfaches komplizierter aus.
 
Davon bin ich überzeugt. Zum einen weist dein Code stilistische Mängel auf, z.B.:
  • Du scheinst keine Ahnung von Algorithmen zu haben. Als einführende Lektüre empfehle ich dir ein Buch von Robert Sedgewick. Für Fortgeschrittene ist das Standardwerk 'The Art of Computer Programming' von Donald Ervin Knuth eine wahre Fundgrube.
  • Du scheinst auch kaum etwas über die Collection-Klassen in java.util zu wissen. Du solltest dich wirklich damit vertraut machen.
  • Wahrscheinlich kennst du auch nicht den ternären Operator ? : mit dessen Hilfe man mitunter ein if-Konstrukt vermeiden kann.
  • Deine Namen für die Variablen und Methoden sind viel zu lang. Sie blähen den Code nur unnütz auf und verringern dadurch auch die Lesbarkeit.
  • Du nutzt weder das 'erweiterte for', noch die Möglichkeit, eine Schleife mit break vorzeitig zu verlassen oder mit continue vorzeitig neu zu starten.
Beispiel (man beachte die korrekte Schreibweise des Methodennamens):
Java:
private int getPositionFromCharInCharset(int location, char[] charsetChars, char[] plainChars)
{
    char plainChar = plainChars[location];
    int index = 0;
    for(byte b : charsetChars) {
        if(b == plainChar) break;
        index++;        
    }
    return index==charsetChars.length ? -1 : index;
}
(Fortsetzung folgt)
 
Meine Hauptklasse wirkt nur deswegen so groß, weil sie viele Kommentarzeilen hat sowie die GUI aufbauen und verwalten muss. Die Run-Methode ist eigentlich relativ kurz und übersichtlich:
Java:
// Dies ist die eigentliche Verarbeitungs-Routine
    public void Run()
    {
        String str = new String(jtf.getText());
        int len = str.length();
        int idx;
        String[] src;
        String dst = "";
        for ( int i = 1; i <= len; i++ )
        {
            vrDistributor vrd = new vrDistributor(len-i,i+1);
            for ( int[] arr1 : vrd )
            {
                idx = 0;
                src = new String[i];
                int cnt = 0;
                for ( int j : arr1 ) {
                    idx += j;
                    if ( cnt >= i ) break;
                    src[cnt] = str.substring(idx, idx+1);
                    idx++; cnt++;
                }
                do {
                    for ( String tmp : src ) dst += tmp;
                    dst += "\n";
                } while ( vrPermutator.createNextPermutation(src) );
            }
        }
        jta.setText(dst);
    }
Die beiden äußeren Schleifen dienen ausschließlich der Zusammenstellung der zu permutierenden Zeichen. Die eigentlichen Permutationen werden nur in der do-Schleife erzeugt und zusammengefasst:
Java:
// Hier wird permutiert
                do {
                    for ( String tmp : src ) dst += tmp;
                    dst += "\n";
                } while ( vrPermutator.createNextPermutation(src) );
Kürzer und übersichtlicher geht es wohl nicht.

(Fortsetzung folgt)
 
Die Hilfsklassen vrDistributor und vrPermutator sind sehr nützlich und universell einsetzbar; man kann sie ohne Änderung in andere Programme übernehmen. Möglicherweise erscheinen sie dir deswegen so kompliziert, weil du weder mit den implementierten Algorithmen vertraut bist (das musst du auch nicht), noch mit den Vorgehensweisen, die sie verwenden bzw. unterstützen (das solltest du aber). Sie arbeiten iterativ und ermöglichen dadurch den Verzicht auf Rekursion; man kann also jederzeit die Verarbeitung ohne überkünstelte Tricks und Techniken abbrechen. Zudem sind sie sehr speicherfreundlich, weil sie in-place arbeiten (d.h. direkt auf den übergebenen Daten arbeiten bzw. keine Hilfs-Arrays brauchen) und immer nur eine Möglichkeit nach der anderen liefern; eine out-of-memory-Exception kann durch sie praktisch nicht verursacht werden.

Die Klasse vrPermutator beinhaltet als einziges Member eine statische Methode und ist daher extrem kurz. Diese Methode implementiert einen Algorithmus, der als SEPA allgemein bekannt ist. Er gilt als effizient und ist deswegen bemerkenswert, weil er die Permutationen in alphabetischer Reihenfolge liefert. Als Parameter kann ein Array jeden Datentyps übergeben werden, welches das Interface Comparable implementiert. Das ist nicht nur bei vielen Standard-Klassen vorhanden, sondern kann auch leicht in selbst geschriebene Klassen eingebaut werden.

Die Klasse vrDistributor ist deswegen so umfangreich, weil sie viele Möglichkeiten bietet, sie zu nutzen. Zudem wendet sie ein allgemeines Prinzip der Programmierung an: grundlegende Funktionalität soll in möglichst wenig Funktionen implementiert werden; andere Funktionen, die darauf aufbauen, müssen dann beim Debuggen und bei anderen Code-Änderungen nur wenig oder sogar gar nicht angepasst werden.
  • Die Klasse hat einen Copy-Konstruktor, mit dem auf einfache Weise eine Kopie eines vorhandenen Objektes erstellt werden kann; dieser wird auch von der überschriebenen clone-Methode genutzt.
  • Sie bietet die Möglichkeit, ausschließlich statische Funktionen zu verwenden, ohne ein Objekt instanziieren zu müssen; die komplette Funktionalität steht trotzdem zu Verfügung.
  • Die Instanziierung eines Objektes hat den Vorteil, dass ein paar Berechnungen und Parametertests entfallen können (was die Verarbeitung ein wenig schneller macht), da die Datenkonsistenz nicht gefährdet werden kann.
  • Sie implementiert einen eigenen Iterator. Dadurch wird es möglich, das 'erweiterte for' auf sie anzuwenden (siehe Hauptklasse). Aber sie bietet auch Funktionen an, mit deren Hilfe man auch ohne den Iterator die verschiedenen Mötglichkeiten sukzessive abrufen kann.
 
Wow, vielen Dank. Stimmt, von Algos habe ich nicht sehr viel Ahnung, weil ich sehr selten welche benötige. Ich habe mir mal "Algorithms in Java Part I - IV" (Fundamentals, Data Structures, Sorting, Searching) besorgt. Wenn ich dann etwas so etwas machen muss, versuche ich es zeilenweiße abzubilden. Wie hier z. B. bei jedem Durchgang wird das letzte Zeichen um 1 "erhöht", ist es das letzte Zeichen des Charsets wird das nächste Feld erhöht und das vorherige resettet usw., ist das zu erhöhende Zeichen ganz links wird der Text verlängert.
Mit Collections kenne ich mich ein wenig aus, aber ich verwende i. d. R. nur ArrayList und HashMap, sehr selten LinkedList o. HashSet (nach dem ob es geordnet sein muss), sonst eigendlich keine. Kannst du mir bitte sagen, was ich dabei falsch gemacht habe?
Den ternären Operator kenne ich, aber mir schien es vorteilhafter die if auf 3 Zeilen aufzuteilen, um es leserlicher zu machen.
Der Name meiner Methoden sind so lange, weil ich damit dem "Don't make me thing"-Prinzip entgegenkommen wollte. Wenn man getPositionFromCharInCharset ließt, weiß man sofort was die Methode macht - Zurückgeben, an welcher Stelle im Charset sich der Char befindet. Wenn ich einen kürzeren Namen verwendet hätte, müsste man erst einmal in der JavaDoc nachlesen was der Code eigendlich macht, oder? Aber der Rechtschreibfehler wäre mir wohl nie aufgefallen. OO'
Ich fand break und continue nie so schön, deshalb habe ich statt ersteren eine boolean zum beenden verwendet und statt zweiteren die Schleife immer komplett durchlaufen lassen. Durch die boolean ist bei mir meistens die Verwendung von extended for's weggefallen.
 
Dass du schon Literatur zu Algorithmen liest, ist lobenswert, aber du solltest sie nicht einfach nur als Quelle zum Abschreiben mißbrauchen. Versuche auch, die Vorgehensweisen zu verstehen, um beurteilen zu können, welche für deine Zwecke am besten geeignet sind, bzw. ob und wie sie für dein Programm abgewandelt werden müssen. Möglicherweise bekommst du dann sogar Ideen, mit denen du eigene Verfahren entwickelst.
Dein immer wiederkehrendes Umkopieren in ein neues Array ist speicherfressend und völlig unnötig. Mit einer LinkedList o.ä. hättest du dir das ersparen können, Mit der add-Methode kannst du auch neue Elemente an einer bestimmten Position einfügen, und du kannst auch das erweiterte for nutzen.
Das erweiterte for ist ein Konstrukt, das man nicht meiden, sondern antreben sollte. Es ist jedem einigermaßen bewanderten Java-Programmierer geläufig, und ermöglicht eine knappe und intuitiv verständliche Formulierung eines Durchlaufens einer Aufzählung. Wenn du einen Index brauchst, kannst du ihn 'von Hand' mitzählen.
Überlange Namen sollte man vermeiden, eine mit dem kurzen und einprägsamen Namen getIndex benannte Methode ist viel einfacher zu lesen und zu verstehen.
Das Verwenden von break und continue solltest du dir angewöhnen. Zum einen ersparen sie längliche if-else-if-Ketten, und zum zweiten helfen sie dem Leser bei der Nachvollziehbarkeit der Funktionsweise, weil sie ihm signalisieren, dass der Rest des Schleifenkörpers für den betreffenden Fall nicht relevant ist.
Der ternäre Operator ist eine elegante Methode, if-Konstrukte zu vermeiden; diese neigen nämlich dazu, den Code schon allein dadurch aufzublähen, dass die Klammern und die else-Anweisung auf einer eigenen Zeile stehen. Hier ist ein Beispiel, wie man ihn für eine knappe und trotzdem lesbare Schreibweise nutzen kann.
Java:
int ret
= (index == charsetChars.length) // die Bedingung darf sogar noch wesentlich länger sein
? -1 // das ist natürlich ein einfacher Fall, aber ein längerer Ausdruck ist auch verkraftbar
: index; // wer will, kann statt dessen das Semikolon auch auf die folgende Zeile verschieben
return ret;

PS: Kennst du den Obfuscated C Contest schon?
 
Zuletzt bearbeitet:
Vielen Dank, ich werde versuchen mich möglichst daran zu halten.
Nein, kannte ich nicht, aber teilnehmen kann ich daran ja leider auch nicht, weil ich die falsche Sprache verwendet habe.
 
Zurück