Best Practice Tiefe Kopie eines Arrays unbekannter Dimension und unbekannten Typs

Complexus

Mitglied
Guten Tag,

ich möchte hier meine Methode zur tiefen Kopie eines Arrays unbekannter Dimension und unbekannten Typs vorstellen. Damit die Kritik mir gegenüber rational und konstruktiv ausfällt, bitte ich potenzielle Antwortgeber, ihre Verbesserungsvorschläge detailreich zu präsentieren und nicht nur allgemein zu beschreiben. Antworten wie "Verwende doch ... oder ..., damit geht es leichter!" sind völlig fehl am Platz. Falls ihr prinzipiell eine elegantere Lösung parat habt, prüft diese bitte ordentlich auf Fehler. Vielen Dank im Voraus für die Berücksichtigung des hier Geschriebenen.

Simples Interface, welches die Methode der tiefen Kopie für einen komplexen Datentypen bereitstellt:

Kopie.Java
Java:
public interface Kopie {

	public Kopie getKopie();
	
}
Die
Code:
.clone()
aus
Code:
Object
ist protected. Auf diese kann von Außen nicht zu gegriffen werden. Folglich können generische Typen, die hier zwingend notwendig sind, auch nicht darauf zugreifen.


Eine Testklasse, welche das Interface implementiert, die Methode des Kopierens korrekt umsetzt und eine neue Instanz des Typs zurückgibt:

TestKlasse.java
Java:
public class TestKlasse implements Kopie {

	char charakter;

	/* Anfang: Konstruktoren */

	public TestKlasse(char charakter) {
		this.charakter = charakter;
	}

	// Standardkonstruktor

	public TestKlasse() {
		this('0');
	}

	// Kopierkonstruktor

	public TestKlasse(TestKlasse Original) {
		this.charakter = Original.getCharakter();
	}

	/* Ende: Konstruktoren */

	/* Anfang: Setter */

	public void setZahlenwert(char charakter) {
		this.charakter = charakter;
	}

	/* Ende: Setter */

	/* Anfang: Getter */

	public char getCharakter() {
		return charakter;
	}

	/* Ende: Getter */

	@Override
	public Kopie getKopie() {
		TestKlasse echteKopie = new TestKlasse(this.charakter);
		return echteKopie;
	}

}

Die Hauptklasse, welche die generische Methode der tiefen Arraykopie durchführt! Eine Methode, welche nur rekursiv funktionieren kann! Bzw. Rekursion und Iteration kombiniert.

Java:
/******************************
 *                            *
 *  @author      Tim Lehmann  *
 *  Datum        18.12.2013   *
 *                            *
 ******************************/

public class Steuerung {

	public static void main(String[] args) {

		ausfuehren();

	}

	public static void ausfuehren() {

		/* Anfang: getestete fehleranfaellige Uebergabeparameter */

		int int_var = 0;
		int[] int_Array_var = new int[1];
		Object Object_var = new Object();
		Object[] Object_Array_var = new Object[1];
		TestKlasse testKlasse_var = new TestKlasse('a');
		TestKlasse[] testKlasse_array_var = new TestKlasse[1];

		/* Ende: getestete fehleranfaellige Uebergabeparameter */

		TestKlasse tk[][][] = new TestKlasse[2][3][3];

		tk[0][0][0] = new TestKlasse('0');
		tk[0][0][1] = new TestKlasse('1');
		tk[0][0][2] = new TestKlasse('2');
		tk[0][1][0] = new TestKlasse('3');
		tk[0][1][1] = new TestKlasse('4');
		tk[0][1][2] = new TestKlasse('5');
		tk[0][2][0] = new TestKlasse('6');
		tk[0][2][1] = new TestKlasse('7');
		tk[0][2][2] = new TestKlasse('8');
		tk[1][0][0] = new TestKlasse('a');
		tk[1][0][1] = new TestKlasse('b');
		tk[1][0][2] = new TestKlasse('c');
		tk[1][1][0] = new TestKlasse('d');
		tk[1][1][1] = new TestKlasse('e');
		tk[1][1][2] = new TestKlasse('f');
		tk[1][2][0] = new TestKlasse('g');
		tk[1][2][1] = new TestKlasse('h');
		tk[1][2][2] = new TestKlasse('i');

		TestKlasse tk_flache_kopie[][][] = TiefeArrayKopie(tk);

		// Ausgabe

		for (int z = 0; z < tk.length; z++) {

			for (int y = 0; y < tk[0].length; y++) {

				for (int x = 0; x < tk[0][0].length; x++) {

					System.out.print(tk[z][y][x].getCharakter() + "   ");

				}

				System.out.println();

			}

			System.out.println("\n");

		}

		for (int z = 0; z < tk.length; z++) {

			for (int y = 0; y < tk[0].length; y++) {

				for (int x = 0; x < tk[0][0].length; x++) {

					System.out.print(tk_flache_kopie[z][y][x].getCharakter()
							+ "   ");

				}

				System.out.println();

			}

			System.out.println("\n");

		}

	}

	@SuppressWarnings("unchecked")
	public static <T> T TiefeArrayKopie(T Array) {

		String Fehlermeldung = null;
		T Kopie = null;

		// Uebergabeparameter ist ein Array

		if (Array.getClass().isArray()) {

			// Pruefen, ob Array Typen eines komplexen Datentypen beinhaltet
			// Meines Wissens nach, nur ueber die Auswertung der Zeichenkette
			// moeglich.
			// Das 'L' in ".getClass().toString()" steht fuer einen komplexen
			// Datentypen

			if (Array.getClass().toString().matches("^.*?\\[L.*\\;")) {

				int arraybereite = ((T[]) Array).length;
				Kopie = (T) ((T[]) Array).clone();

				// Iterativ alle Elemente dieses Arrays durchlaufen

				for (int z1 = 0; z1 < arraybereite; z1++) {

					// Untere Dimension enthaelt keine nicht instanziierte
					// Elemente (null-Werte)

					if (((T[]) Array)[z1] != null) {

						// Untere Dimension enthaelt ebenfalls Arrays

						if (((T[]) Array)[z1].getClass().isArray()) {

							// Rekursiver Aufruf der Methode

							((T[]) Kopie)[z1] = TiefeArrayKopie(((T[]) Kopie)[z1]);

						}
						// Untere Dimension enthaelt keine Arrays
						else {

							// Untersten Elemente des Arrays implementieren die
							// Schnittstelle zur tiefen Kopie

							if (((T[]) Array)[0] instanceof Kopie) {

								for (int z = 0; z < arraybereite; z++) {

									((T[]) Kopie)[z] = (T) ((Kopie) ((T[]) Kopie)[z])
											.getKopie();

								}

								// Untersten Elemente des Arrays implementieren
								// keine Schnittstelle zur tiefen Kopie

							} else {

								Fehlermeldung = "Elemente des Uebergabeparameters impelementieren nicht die noetige Schnittstelle!";

							}

						}

					}

					// Untere Dimension enthaelt nicht instanziierte Elemente

					else {

						Fehlermeldung = "Uebergabeparameter enthält nicht instanziierte Elemente (null Werte)";

					}
				}

				// Array enthaelt keine komplexen Datentypen

			} else {

				Fehlermeldung = "Uebergabeparameter darf kein Array primitiver Datentypen sein!";

			}

			// Uebergabeparameter ist kein Array

		} else {

			Fehlermeldung = "Uebergabeparameter muss ein Array sein!";

		}

		// Wurf der Ausnahme

		if (Fehlermeldung != null) {

			throw new IllegalArgumentException(Fehlermeldung);

		}

		return Kopie;

	}

}

Sicher kann man die Verzweigungen zusammenfassen. Ich wollte hier dem Benutzer der Methode hinreichend auf seine Fehler hinweisen.
 
Zuletzt bearbeitet:

rme

Top Contributor
Hallo,

Eine Methode, welche nur rekursiv funktionieren kann!

So etwas gibt es nicht. Man kann jede rekursive Methode in eine iterative verwandeln, das ist ein zentrales Ergebnis der Informatik.

Deine Methode kann doch nun nur Arrays kopieren, deren Inhalte "Kopie" implementieren, oder? Wozu sind dann der Hack mit dem Klassennamen und die ungetypten Dinge nötig? Dank Polymorphie kannst du die Objekte doch alle auf "Kopie" casten.

Für welche Zwecke findet diese Methode eigentlich Anwendung? Hast du das nur aus Interesse gemacht oder brauchst du das irgendwo für? Ich war noch nie in einer Situation, in der etwas derartiges getan werden musste..
 

Complexus

Mitglied
So etwas gibt es nicht. Man kann jede rekursive Methode in eine iterative verwandeln, das ist ein zentrales Ergebnis der Informatik.
Wie sollte man dann eine Methode entwickeln, die unbekannt dimensionierte Arrays verwaltet? Um auf die Elemente des Arrays zur Laufzeit zugreifen zu können, sind so viele Schleifen, wie Dimensionen nötig. Ich weiß doch vorher nicht, wie viele Dimensionen das Array desjendigen hat, welcher diese Methode aufruft.
Nur durch Rekursion ist es mir möglich, dem Array eine Dimension zu nehmen und die restlichen Dimensionen mit der selben Methode zu verarbeiten.

Deine Methode kann doch nun nur Arrays kopieren, deren Inhalte "Kopie" implementieren, oder?
Richtig!

Wozu sind dann der Hack mit dem Klassennamen und die ungetypten Dinge nötig?
Damit ich als Übergabeparameter Arrays jeglicher Dimension verwenden kann. Der Typ ist tatsächlich statisch, dahingehend sind die generischen Typen unnötig, werden aber bei der Übergabe eines Array essentiel. Der generische Typ kann X-dimensionierte Arrays annehmen.

Also
Code:
new Klasse[]
,
Code:
new Klasse[][]
,
Code:
new Klasse[][][]
, usw.

Nur dafür ist die Verwendung generischer Typen gedacht und nicht etwa, weil ich jeden (End)Typen zulassen will. Nein, ich will natürlich nur Typen, die meine Schnittstelle (hier Kopie) implementieren, zu lassen.

Der Hack mit den Klassennamen ist zum Ausgleichen der Schwierigkeiten, welche die generischen Typen mit sich bringen.

Dank Polymorphie kannst du die Objekte doch alle auf "Kopie" casten.
Das ist richtig, aber das Problem der Übergabe eines Arrays jeglicher Dimension ist damit nicht gelöst. Bzw. ist mir keine Lösung klar.

Kannst Du mir eine Methode präsentieren, der ich sowohl
Code:
new Klasse[]
,
Code:
new Klasse[][]
,
Code:
new Klasse[][][]
, usw. übergeben kann. (Klasse instanceof Schnittstelle).

Für welche Zwecke findet diese Methode eigentlich Anwendung? Hast du das nur aus Interesse gemacht oder brauchst du das irgendwo für? Ich war noch nie in einer Situation, in der etwas derartiges getan werden musste.

Ursprünglich aus Interesse, da ich die Programmiermechanik und -Möglichkeiten besser verstehen möchte. Mittlerweile empfinde ich es als angenehm, eine Methode zu haben, die unabhängig von der Dimension des Arrays alle Arrays tiefenkopiert.

Ich habe zwar noch nie mit Matrizen operiert, deren Dimension größer als zwei waren, aber schon so spart man sich unnötige Methoden ähnlichen Inhalts.
 
Zuletzt bearbeitet:

rme

Top Contributor
Zur Rekursion: Dafür benutzt man eine Datenstruktur, zum Beispiel einen Stack. Wenn die untere Dimension wieder ein Array ist, legst du es erstmal auf den Stack und gehst eine Ebene tiefer. So lange, bis du zu den Elementen vordringst. Die werden dann bearbeitet - und danach alles, was noch auf dem Stack liegt. So macht es Java zur Laufzeit auch - die lokalen Variablen deiner Methode verbleiben auf dem Stack, sodass sie nach dem rekursiven Aufruf wieder zur Verfügung stehen.

Zum Anwendungsfall: Große oder vieldimensionale Matrizen speichert man üblicherweise nicht als mehrdimensionales Array, sondern entwirft eine eigene Datenstruktur dafür, die die Dimesionen in Listen verwaltet. Die erste Dimension ist eine Liste von Listen, die wiederum die Listen der dritten Dimesion enthält usw. Durch die Verwendung einer eigenen Datenstruktur kann man dann natürlich die Dimensionen speichern und eine Kopie vernünftig implementieren.

Abgesehen vom Umschreiben der Rekursion in eine Iteration fällt mir auch keine bessere Lösung ein. Allerdings haben deine Variablen sehr ungünstige Namen - "Array" und "Kopie" sind in deinem Programm beides gültige Klasennnamen, deshalb würde ich sie auf keinen Fall als Variablennamen verwenden, das hat mich beim Lesen oft verwirrt. Durch den Einsatz der Methode Arrays.getLength kannst du dir viele Type-Casts sparen. Hier hat jemand eine generische Lösung vorgeschlagen, die ähnlich funktioniert: How to elegantly copy multidimensional arrays in Java? - Stack Overflow
Aber auch dort ist man sich einig, dass man sowas in der Praxis nicht benötigt.
 

Complexus

Mitglied
Wie Rekursion funktioniert, weiß ich.

Dass man anstelle Mehrdimensionale Arrays für gewöhnlich Listen verwendet, ist mir auch bekannt. Wenn gleich Listen jedoch viel speicherintensiver sind. In heutiger Zeit aber kein Problem mehr, da RAM gegenwärtig als spottbillig gilt. Folge dessen sind immer speicherschlamprige Programme. Und so schließt sich der Teufelskreis. ;)

Abgesehen vom Umschreiben der Rekursion in eine Iteration fällt mir auch keine bessere Lösung ein.
Ich bin nach wie vor davon überzeugt, dass es keine iterative Lösung hierzu gibt. ;)

Gut, die Namen der Variablen waren jetzt nur provisorisch gewählt. Und was Du meinst, ist
Code:
Array.getLength(Object array)
aus Reflections. Ohne 's'. Wo da jetzt Typecasts wegfallen sollen, weiß ich nocht nicht. Ist ja auch egal. Es wird seinen praktischen Nutzen schon finden.

Ich danke Dir sehr für Dein Interesse an dem Thema und dafür, dass Du Dich zu Wort gemeldet hast.

Grüße
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
C Methode für tiefe Kopie eines unbekannt (dimensionierten & typisierten) Feldes realisierbar? Allgemeine Java-Themen 7
E tiefe Kopie nicht serialisierbarer Objekte Allgemeine Java-Themen 3
S Tiefe Kopie einer Baumstruktur als statische Methode Allgemeine Java-Themen 8
U Tiefe Objekte schreiben Allgemeine Java-Themen 1
D Datenstruktur für Hierarchie/Baum mit Tiefe 3 Allgemeine Java-Themen 8
B tiefe gleichheit / flache gleichheit Allgemeine Java-Themen 5
W Tiefe eines Knotens Allgemeine Java-Themen 3
M Verschachtelte Schleifen (unbekannte Tiefe) Allgemeine Java-Themen 3
Linad Tiefe der Rekursion als Abbruchbedingung Allgemeine Java-Themen 6
M Neue Instanz/Kopie einer unbekannten Klasse Allgemeine Java-Themen 7
reibi Kopie einer Liste Allgemeine Java-Themen 4
J Collections in Instanzattributen als Kopie übergeben Allgemeine Java-Themen 4
N for (String s : StringArray) : Kopie oder Verweis? Allgemeine Java-Themen 9
M "Kopie" von JLabels werden nicht gezeichnet Allgemeine Java-Themen 10
G Kopie eines FileReader Allgemeine Java-Themen 4
Z Echte Kopie eines Objektes erstellen Allgemeine Java-Themen 12
O regulärer Ausdruck zum durchsuchen eines Strings verwenden Allgemeine Java-Themen 2
T Rotationswinkel eines Bildes bestimmen Allgemeine Java-Themen 4
C Probleme beim Erstellen eines runnable-jar files Allgemeine Java-Themen 1
J JavaScript innerhalb eines Java Projekts ausführen Allgemeine Java-Themen 2
Encera Größe eines Objektes in Byte berechnen Allgemeine Java-Themen 2
8u3631984 Prüfen ob min. ein Element eines Sets in einem anderen Set enh Allgemeine Java-Themen 4
M Array Rang eines Elements Allgemeine Java-Themen 4
OnDemand Teile eines Links entfernen Allgemeine Java-Themen 6
H Auslesen eines (LDAP-)Attributs in Active Directory Allgemeine Java-Themen 2
W JSON parsen eines ,mit JS.stringify erstellten Strings Allgemeine Java-Themen 27
H Textposition eines gedrehten Textes verschieben Allgemeine Java-Themen 8
berserkerdq2 run-methode eines Threads so programmieren, dass 30x die Sekunde etwas ausgeführt wird. Allgemeine Java-Themen 44
E Ersetzen eines Bildes in der Kopfzeile eines Word-Docx-Dokuments mit Apache POI XWPF Allgemeine Java-Themen 0
N Fahrtrichtung eines selbstfahrenden Auto ändern Allgemeine Java-Themen 3
T Letztes Zeichen eines Strings enfernen Allgemeine Java-Themen 14
S Übergabe eines Sortierkriteriums für ein Artikel Array mittels BiPredicate<Artikel, Artikel> Allgemeine Java-Themen 13
gotzi242 Schatzsuche mithilfe eines O(log n) Algorithmus Allgemeine Java-Themen 2
C Koordinaten LONG/LAT eines neuen Punktes in bestimmter Entfernen und Winkel berechnen Allgemeine Java-Themen 3
Tobero Meine Funktion für das beinhalten eines Punktes in einem Kreis funktioniert nicht Allgemeine Java-Themen 5
LimDul Direktes return eines Array geht nicht Allgemeine Java-Themen 20
S Mittelwert anhand eines Stream berechnen Allgemeine Java-Themen 5
kodela Breite eines erweiterten Monitors feststellen Allgemeine Java-Themen 5
R Zeilen eines 2d Arrays abwechselnd links und rechts mit Nullen auffüllen Allgemeine Java-Themen 14
Zrebna Alternative Darstellung eines Codesnippets Allgemeine Java-Themen 33
kodela Inhalt eines Arrays ändert sich mysteriös Allgemeine Java-Themen 2
bueseb84 Wget mit Wildcards - oder wie lädt man bei JFrog die letzte Version eines Artifacts herunter Allgemeine Java-Themen 3
N Erkennen eines Programs Allgemeine Java-Themen 2
N Pausieren eines Programmes Allgemeine Java-Themen 4
D Input/Output Implementierung eines CommandHandlers/Parsers für viele Eingaben Allgemeine Java-Themen 26
M Gibt es eine API die den aktuellen Wert eines Indikators beim Trading zurückgibt? Allgemeine Java-Themen 7
F Wie bekommt man alle Filenamen eines Webserver Verzeichnisses Allgemeine Java-Themen 6
A Fehler beim Öffnen eines Projekts Allgemeine Java-Themen 6
N Eigenschaften eines Buttons per Setter verändern Allgemeine Java-Themen 5
S Ausfuehrung eines Programms aufzeichnen..? Allgemeine Java-Themen 4
X Ermittlung eines doppelte Paars mit Streams Allgemeine Java-Themen 50
Stonie Prüfen von direkter Implementierung eines Interfaces Allgemeine Java-Themen 7
S Vorbereitung eines Praktikums Allgemeine Java-Themen 4
H Aufruf eines Web Service anhand übergebenen Parameter Allgemeine Java-Themen 2
M Weiterleiten von empfangenen Nachrichten eines StompSessionHandlers Allgemeine Java-Themen 1
J Programm zum Suchen eines Wortes im Dateisystem Allgemeine Java-Themen 4
H Rename eines Projekts Allgemeine Java-Themen 1
J Fenstergröße eines anderen Programmes auslesen Allgemeine Java-Themen 9
ReinerCoder auf Klassen innerhalb eines package zugreifen Allgemeine Java-Themen 22
Meeresgott Erste Schritte Sourcetree - Git | Suchen eines Commits Allgemeine Java-Themen 2
E Status eines USB Mikrofon abfragen Allgemeine Java-Themen 2
DaCrazyJavaExpert OOP Ansätze und Tipps zum Porgrammieren eines Taschenrechners Allgemeine Java-Themen 25
A OOP Problem beim Berechnen der größten Fläche eines Ringes Allgemeine Java-Themen 19
JavaNewbie2.0 Start eines Anderen Programm erkennen Allgemeine Java-Themen 6
I Verbindung eines Java-Plugins mit Webserver Allgemeine Java-Themen 3
L Auswertung eines Testes funktioniert nicht Allgemeine Java-Themen 37
G Iteratoren - Wie kann man mithilfe von Iteratoren nur jeden zweiten Wert eines TreeSets ausgeben? Allgemeine Java-Themen 4
GreenTeaYT Elemente eines 2Dim LinkedList von links nach rechts ausgeben? Allgemeine Java-Themen 0
B Spalten eines 2d-Arrays Allgemeine Java-Themen 2
M Rechenprogramm eines wissenschaftlichen Taschenrechners Allgemeine Java-Themen 4
S Eigenschaften (hier Verknüpfung) eines Files lesen Allgemeine Java-Themen 2
E Typüberprüfung eines chars Allgemeine Java-Themen 5
H Hilfe bei Erstellung eines Hilfe Fenster bei Tastendruck (F1 bei Win98) Allgemeine Java-Themen 5
T Teile eines Double-Wertes verändern Allgemeine Java-Themen 2
R Rückgabe eines Arrays durch Funktion Allgemeine Java-Themen 9
H Datentypen Typ eines Arrays überprüfen Allgemeine Java-Themen 9
RalleYTN DPI eines Bildes ändern Allgemeine Java-Themen 4
N Methoden Methoden einer Klasse auf Grundlage eines Strings aufrufen Allgemeine Java-Themen 6
K Bestimmten Bereich eines Strings lesen Allgemeine Java-Themen 6
C -Verschiedene Versionen eines Programms verwalten Allgemeine Java-Themen 7
O Datentypen Erstellung eines Containers, der verschachtelte Map-Strukturen beherbergen kann Allgemeine Java-Themen 0
A einmalige Ausführung eines Methodenabschnittes Allgemeine Java-Themen 3
F CPU Last eines Thread ausfindig machen Allgemeine Java-Themen 0
L Menge der Buchstaben eines Textes zählen Allgemeine Java-Themen 3
F Teil eines Bildes laden Allgemeine Java-Themen 1
Neumi5694 Operatoren regEx für das Erstellen eines Strings verwenden Allgemeine Java-Themen 3
P Löschen eines keys in einer SortedMap Allgemeine Java-Themen 5
RalleYTN Input/Output URL eines Zip Entry? Allgemeine Java-Themen 2
S Alle Methodenaufrufe eines Threads notieren..? Allgemeine Java-Themen 7
J NullPointerExeption bei Inizialisierung eines Arrays Allgemeine Java-Themen 3
I Setzen und Lesen eines Objektes (Enum?) Allgemeine Java-Themen 10
L Implementierung eines AVT-Baums Allgemeine Java-Themen 2
D Größe der Zahlenkombinationen eines Arrays begrenzen Allgemeine Java-Themen 3
D Objekt entlang eines Funktionsgraphens bewegen Allgemeine Java-Themen 6
B Existenz eines Files max 30 sec prüfen Allgemeine Java-Themen 5
U Koordinaten alle Pixel eines Dreiecks zeichnen ausgeben Allgemeine Java-Themen 5
P Wie funktioniert das Feedback eines Klicks auf eine Java GUI Allgemeine Java-Themen 10
thet1983 nach teilen eines Dateinamens suchen Allgemeine Java-Themen 6
T Bezeichnung eines Objektes mit String/int kombinieren Allgemeine Java-Themen 3
Z Auswerten eines eingegeben Termes Allgemeine Java-Themen 13

Ähnliche Java Themen

Neue Themen


Oben