Generische Arrays durch Typecast mit Object-Array

E

Eikonium

Gast
Gott zum Gruße, liebe Java-Kenner.
Ich bin Informatik-Student, wir behandeln zurzeit Collections in Java.
Eine Übungsaufgabe verlangt von uns, dass wir das folgende Interface implementieren:

Java:
public interface Dictionary<E> {
	public void insert (E e); // Einfuegen von Element e
	public void remove (E e); // Entfernen von Element e
	public boolean contains (E e); // Existenz pruefen
}

Es soll sich dabei um eine Klasse handeln, deren Objekte viele Objekte eines beim Erzeugen bestimmten Typs speichern können.
Der Haken an der Sache: Wir sollen die Aufgabe mit normalen Arrays lösen, es sind keine ArrayLists oder sonstwas erlaubt. Man muss also neue Arrays mit neu bestimmter Größe erstellen, wenn die Anzahl der zu speichernden Dinge die Größe des Arrays überschreitet.
Aber Java unterstützt ja keine generischen Arrays, deren Typ man zur Laufzeit erst festlegt.
Also dachte ich an den Workaround, ein Objekt-Array zu erstellen und dann einen Typecast auf den gewünschten Typ durchzuführen.
Daran bin ich aber gescheitert.. ich bekomme immer eine java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; at MyDictionary.main(MyDictionary.java:61)
(wobei String der Typ ist, den ich in der Main-Methode testweise ausprobiert habe).

Ich weiß überhaupt nicht mehr, woran das liegen könnte...

Mein Code (in den Zeilen 7 und 27 werden mittels (E[]) Typecasts durchgeführt):
Java:
public class MyDictionary<E> implements Dictionary<E> {
//Attribute
	private E[] array; //speichert die Objekte
	private int size; //gibt die belegte Größe des Arrays an, also gleichzeitig den Indey des ersten folgenden freien Platzes
//Konstruktor
	public MyDictionary() {
		this.array = (E[]) new Object[10]; //hier Typecast
		this.size = 0;
	}
//Methoden
	//prüft, ob ein Element enthalten ist
	public boolean contains (E e){
		for(int i=0; i<this.size;i++) {
			if (e == this.array[i]){
				return true;
			}
		}
	return false;
	}
	//Insert fügt ein Element am Ende des Arrays ein	
	public void insert (E e){
		if (this.array.length == this.size) { //wenn das momentane Array maximal gefüllt ist
				Object[] greaterarray = new Object[this.array.length*2]; //wird ein doppelt so großes Objektarray erstellt
				for (int i =0; i<this.array.length; i++) { //die Einträge werden kopiert
						greaterarray[i]=this.array[i];
				}
				this.array = (E[]) greaterarray; //das neue Array wird typegecastet und dem Attribut zugewiesen
		}
		this.array[this.size] = e;
		this.size++;
	}
	
	//entfernen alle Elemente e
	public void remove (E e){
		for (int i =0; i<=this.size;i++) {
				if (this.array[i] == e) {
					for (int j=i; j<=this.size-1;j++) {
							this.array[j]=this.array[j+1];
					}
					this.size=this.size-1;
				}
		}
	}
	//Stringdarstellung
	public String toString() {
		String result = new String();
		result = "[ ";
		for (int i=0;i<=this.size;i++){
				result = result + this.array[i];
				if (i<this.size) result=result + ",";
		}
		result = result + " ]";
		return result;
	}
	//main Methode... hier wird irgendwo der Fehler aufgerufen
	public static void main(String[] args) {
		MyDictionary<String> test = new MyDictionary();
		for (Integer i = 0;i<20;i++) {
			test.insert(Integer.toString(i*100));}
		for (Integer i=0; i<5;i++) {
			test.remove(Integer.toString(i*200));}
		for (Integer i=0; i<test.size;i++) {
		System.out.println(test.array[i]);}
	}
}

Ich danke für eure Mithilfe,
Eikonium
 

kay73

Bekanntes Mitglied
In der Tat äußerst fies, Erklärung hier:
Java: Generics, arrays, and the ClassCastException - Stack Overflow

Ich kenne nur zwei Alternativen:

  • Workaround beibehalten und Zugriff auf Array-Elemente per
    Code:
    get
    ter:
    Java:
    	public E getIndex(int index) {
    		return (E) array[index];
    	}
  • Tatsächlich ein generisches Array erzeugen (Code aus dem Link aus dem Post von oben):
    Java:
    import java.lang.reflect.Array;
    // ...
    @SuppressWarnings("unchecked")
    public MyDictionary(final Class<E> clazz) {
    	array=(E[]) Array.newInstance(clazz,512);	
    	this.size = 0;
    }
    
    // main(...)
    MyDictionary<String> test = new MyDictionary<String> (String.class);

IMHO ist diese Aufgabe grosser Mist: Verwirrt beginnende Java-Programmierer, verleitet zu schlampigem Code und hat keinerlei pädagogischen Nutzen. Oder fragen die wenigstens nach sinnvollen Platz-Strategien?
 
Zuletzt bearbeitet:

Landei

Top Contributor
IMHO ist diese Aufgabe grosser Mist: Verwirrt beginnende Java-Programmierer, verleitet zu schlampigem Code und hat keinerlei pädagogischen Nutzen. Oder fragen die wenigstens nach sinnvollen Platz-Strategien?

Full ack.

Die Klasse als Konstruktorparameter zu übergeben ist ein hässlicher, aber häufig angewandter Trick. Man kann man das allerdings oft vermeiden, wenn man die Array-Erzeugung solange hinauszögert, bis man ein E-Objekt in der Hand hält. Eine "Minimallösung":

Java:
import java.lang.reflect.Array;

public class ArrayDictionary<E> implements Dictionary<E> {

    private final int capacity;
    private E[] array;

    public ArrayDictionary(int capacity) {
        this.capacity = capacity;
    }

    public void insert(E e) {
        if(array == null) {
            array = (E[]) Array.newInstance(e.getClass(), capacity);
        } 
        for(int i = 0; i < array.length; i++) {
            if(array[i] == null) {
                array[i] = e;
                return;
            }
        }
        throw new ArrayIndexOutOfBoundsException();
    }

    public void remove(E e) {
        if(array != null) {
            for(int i = 0; i < array.length; i++) {
                if(array[i] == e) {
                    array[i] = null;
                    return;
                }
            }
        }
    }

    public boolean contains(E e) {
        if(array != null) {
            for(E elem : array) {
                if (e.equals(elem)) {
                    return true;
                }
            }
        }
        return false;
    }

    //Test
    public static void main(String... args) {
        Dictionary<String> dic = new ArrayDictionary<String>(10);
        dic.insert("one");
        dic.insert("two");
        System.out.println(dic.contains("one"));
        System.out.println(dic.contains("two"));
        System.out.println(dic.contains("three"));
        dic.remove("two");
        dic.remove("three");
        System.out.println(dic.contains("one"));
        System.out.println(dic.contains("two"));
        System.out.println(dic.contains("three"));
    }
}

Arrays haben einen sehr beschränkten Einsatzbereich: Da, wo es unbedingt schnell gehn muss und zum Erstellen besserer Datenstrukturen. Ansonsten sind sie die Pest und ein klaffendes Loch im Typsystem.

Würde die Aufgabenstellung nicht ausdrücklich ein Array fordern, würde ich eine einfach verkettete Liste vorschlagen.
 
Zuletzt bearbeitet:

Wortraum

Bekanntes Mitglied
Wieso machst Du es nicht mit einem Object‐Feld? Die Idee ist nämlich richtig. Da alle Deine Methoden nur Objekte des Typs E akzeptieren, kann niemals etwas anderes im Feld landen; Du kannst jederzeit ein Objekt dort herausholen und in E umwandeln.

Das ist gängige Praxis – schau beispielsweise mal in ArrayList – und hat nur einen Schönheitsfehler: der Übersetzer weiß nicht, was Du weißt, nämlich daß im Object‐Feld nur E sein kann; folglich warnt er wegen der ungeprüften Typwandlung. In Java kann man dagegen nichts tun, außer die Warnung zu unterdrücken.

Hier ein Minimalbeispiel ohne Prüfung auf Initialisierung, Größe des Feldes oder Nullwerte. Aus Faulheit habe ich exemplarisch die Methoden setElement und getElement gewählt:
Java:
public class ArrayDictionary<E> implements Dictionary<E> {
    private Object[] dictionary;

    public void setElement(int index, E element){
        dictionary[index] = element;
    }

    @SuppressWarnings("unchecked")
    public E getElement(int index){
        return (E) dictionary[index];
    }

    public boolean contains(E e) {
        for (Object o : dictionary) {
            if (e.equals(o)) { /* oder e == o, was auch immer die Aufgabe verlangt */
                return true;
            }
        }
        return false;
    }
…
}
Für weitere Erklärungen, um das Problem zu verstehen, hilft Dir vielleicht folgender Artikel:
AngelikaLanger.com - Java Generics - Generic Creation - Angelika Langer Training/Consulting
 
Zuletzt bearbeitet:

Landei

Top Contributor
Na ja, es gibt schon einen kleinen Vorteil bei meiner Variante:
Java:
        ArrayDictionary<String> dic = new ArrayDictionary<String>();
        dic.setElement(1, "x");
        ArrayDictionary<Integer> dic1 = (ArrayDictionary)dic; //OK, das ist böse
        dic1.setElement(2, 42);  //mein Code würde hier meckern
        //Jahre gehen ins Land, und dic1 ist inzwischen irgendwo hinter den sieben Bergen
        System.out.println(12 * dic1.getElement(1)); //dein Code meckert erst jetzt
 
E

Eikonium

Gast
Danke für eure Arbeit, Antworten, Links und alles andere :)
Ihr habt mir sehr geholfen, konnte aus jedem Beitrag die eine oder andere Finesse erlernen.
Mein Programm funktioniert jetzt, wie es soll.
Dafür waren notwendig:
  • Die aus dem Array entnommenen Objekte explizit in den Typ E casten (dachte, das wäre nach dem Casten des kompletten Arrays von Object[] zu E[] erledigt, aber nix da!) und via Getter-Methode ansprechen, anstatt auf das Attribut direkt zuzugreifen.
  • Vergleiche zwischen einem neuen Element mit aus dem Array entnommenen (nach E gecasteten) Objekten nicht mit ==, sondern mit Objekt1.equals(Objekt2) durchführen, da == hier immer false liefert (warum auch immer).

Der Code sieht jetzt folgendermaßen aus:
Java:
public class MyDictionary<E> implements Dictionary<E> {
//Attribute
	private E[] array;
	private int size;
//Konstruktor
	public MyDictionary() {
		this.array = (E[]) new Object[10];
		this.size = 0;
	}
//Methoden
	//getter-Methode
	public E getIndex(int index) {
		return (E) this.array[index];
	}
	//prueft, ob ein Element enthalten ist
	public boolean contains (E e){
		for(int i=0; i<this.size;i++) {
			if (e.equals(this.getIndex(i))){
				return true;
			}
		}
	return false;
	}
	//Insert fuegt ein Element am Ende des Arrays ein	
	public void insert (E e){
		if (this.array.length == this.size) {
				Object[] help = new Object[this.array.length*2];
				for (int i =0; i<this.array.length; i++) {
						help[i]=this.array[i];
				}
				this.array = (E[]) help;
		}
		this.array[this.size] = e;
		this.size++;
	}
	
	//entfernen alle Elemente e
	public void remove (E e){
		for (int i =0; i<this.size;i++) {
				if (e.equals(this.getIndex(i))) {
					for (int j=i; j<this.size-1;j++) {
							this.array[j]=this.getIndex(j+1);
					}
					this.size=this.size-1;
				}
		}
	}
	//Stringdarstellung
	public String toString() {
		String result = new String();
		result = "[";
		for (int i=0;i<this.size;i++){
				result = result + this.getIndex(i);
				if (i<this.size-1) result=result + ",";
		}
		result = result + "]";
		return result;
	}
	//main Methode
	public static void main(String[] args) {
		System.out.println("Beispiel 1: Es wird ein MyDictionary<String> erstellt und mit String-Zahlen zwischen 0 und 1900 gefuellt (100er Schritte). Dann werden alle geraden Zahlen bis 800 wieder entfernt und die Existenz von 300 wird geprueft.");
		MyDictionary<String> Beispiel1 = new MyDictionary();
		for (Integer i = 0;i<20;i++) {
			Beispiel1.insert(Integer.toString(i*100));}
		for (Integer i=0; i<5;i++) {
			Beispiel1.remove(Integer.toString(i*200));}
		System.out.println(Beispiel1);
		System.out.println(Beispiel1.contains("300"));
		System.out.println("Beispiel 2: Dasselbe mit Typ Integer, Zahlen bis 4900, Entfernen gerader Zahlen bis 1800, Pruefen von 300");
		MyDictionary<Integer> Beispiel2 = new MyDictionary();
		for (Integer i = 0;i<50;i++) {
			Beispiel2.insert(i*100);}
		for (Integer i=0; i<10;i++) {
			Beispiel2.remove(i*200);}
		System.out.println(Beispiel2);
		System.out.println(Beispiel2.contains(300));
	}
}

madomat (den Threadersteller mit dem ähnlichen Problem) kenne ich auf Anhieb übrigens nicht, aber möglicherweise ist er ein Kommilitone, wer weiß :=)

Die verlinkten Artikel sind allesamt hilfreich gewesen, werde sie in den nächsten Tagen genauer studieren.

Herzlichen Dank für eure Mühe.

Mit freundlichen Grüßen,
Eikonium
 

Wortraum

Bekanntes Mitglied
Überlege Dir mal, was passiert, wenn Du contains aufrufst und als Parameter null übergibst; da gibt es bei Dir nämlich ein Problem. Das gleiche Problem hast Du bei remove(…).

Und noch zwei Tipps:
1) Die Klasse Arrays bietet Methoden an, um Felder oder Teile davon zu kopieren: Arrays (Java Platform SE 6)
2) Strings lassen sich zwar mit Plus miteinander verbinden, doch wenn man das häufig hintereinander macht, ist die Klasse StringBuilder effizienter und oft auch komfortabler.
 

Wortraum

Bekanntes Mitglied
Na ja, es gibt schon einen kleinen Vorteil bei meiner Variante: …
Ich behaupte erst einmal, daß Deine Variante gar nicht funktioniert. :noe: Zeile 14 ist der Bösewicht!
Java:
ArrayDictionary<Number> dict = new ArrayDictionary<Number>();
dict.insert(42); /* erzeugt in Zeile 14 ein Integer-Feld, nicht Number! */
dict.insert(42f); /* und hier ist dann Schluß mit Lustig */
 
Zuletzt bearbeitet:

nixnick

Mitglied
unabhängig zum originalvorschlag hätte ich einen komplett kontraproduktiven, aber um einiges effektiveren vorschlag: wie Landei auch schon gesagt hat, ist da eine etwas komplexere datenstruktur viel schöner.
wir haben da in der schule eine einfach verkettete liste gemacht, mit 3 klassen:
Liste, Knoten und dem Interface Datenelement, jeder knoten hatte einen Knoten als nachfolger und ein Datenelement als "inhalt".
wenn es wirklich um ein wörterbuch oder ähnliches geht, wird wohl eine baumstruktur(2 oder mehr nachfolger pro knoten) oder eine hashtable(index mit quersummen) besser sein, die hastable kann dann aber schon komplexer werden (ist aber übrigens in den collections sehr schön ausprogrammiert ;D und dann auch richtig schnell, selbst bei immensen datenmengen)
 

Landei

Top Contributor
Ich behaupte erst einmal, daß Deine Variante gar nicht funktioniert. :noe: Zeile 14 ist der Bösewicht!
Java:
ArrayDictionary<Number> dict = new ArrayDictionary<Number>();
dict.insert(42); /* erzeugt in Zeile 14 ein Integer-Feld, nicht Number! */
dict.insert(42f); /* und hier ist dann Schluß mit Lustig */

Stimmt, also muss man doch das Klassen-Objekt im Konstruktor übergeben, um den richtigen Array-Typ zu erhalten. Ich nehme alles zurück und behaupte das Gegenteil!
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
N 2 dimensionale generische Arrays Java Basics - Anfänger-Themen 9
0x7F800000 generische arrays mal wieder )-; Java Basics - Anfänger-Themen 6
I Generische Funktion Java Basics - Anfänger-Themen 3
B Generische Typen für dynamisches Formular Java Basics - Anfänger-Themen 3
A Generische Klassen/Interface Java Basics - Anfänger-Themen 1
H Generische Konstruktor Java Basics - Anfänger-Themen 12
Kirby.exe Generische Objekt Instanz erstellen Java Basics - Anfänger-Themen 14
D Generische Klasse Java Basics - Anfänger-Themen 6
H linkedlist generische klassen Java Basics - Anfänger-Themen 169
M Datentypen Generische Datentypen - Syntax Java Basics - Anfänger-Themen 25
O Generische Typen Java Basics - Anfänger-Themen 9
M Generische Klassen "FlaschenRegal" Java Basics - Anfänger-Themen 13
Queiser Datentypen 2 generische Datentypen für eine Schnittstelle Java Basics - Anfänger-Themen 1
M Generische Liste aus Comparable-Objekten Java Basics - Anfänger-Themen 6
J Sortierte generische Liste Java Basics - Anfänger-Themen 1
D statische generische Methoden Java Basics - Anfänger-Themen 3
S Wie muss ich die Generische Methode schreiben? Java Basics - Anfänger-Themen 6
M Methoden Generische Klasse - ändern einzelner Attributwerte Java Basics - Anfänger-Themen 2
S generische methode mit verschiedenen datentypen Java Basics - Anfänger-Themen 3
N Generische Schnittstellen Java Basics - Anfänger-Themen 2
B generische LinkedList nach Häufigkeit der Elemente füllen Java Basics - Anfänger-Themen 6
D Generische Typen Java Basics - Anfänger-Themen 20
S Erste Schritte Generische Klassen sind toll ....aber warum sollte ich das je benutzen? Java Basics - Anfänger-Themen 3
L Generische Liste Java Basics - Anfänger-Themen 4
B Generische Queue programmieren Java Basics - Anfänger-Themen 5
S Generische Methode soll Objekte als Parameter erlauben die bestimmtes Interface implementieren^ Java Basics - Anfänger-Themen 9
A Probleme mit MergeSort Generische Liste Java Basics - Anfänger-Themen 0
A Generische Methode Java Basics - Anfänger-Themen 4
H Collections List in List<SpecificType> als stat. generische Methode zurückgeben Java Basics - Anfänger-Themen 4
J Probleme mit static generische Klasse Java Basics - Anfänger-Themen 6
M Generische Liste Java Basics - Anfänger-Themen 4
B Generische Methode Java Basics - Anfänger-Themen 2
B Generische Klasse Java Basics - Anfänger-Themen 7
B Generische Methoden Java Basics - Anfänger-Themen 8
F Collections Generische Klasse/Methoden Java Basics - Anfänger-Themen 19
L Generische Warteschlange Java Basics - Anfänger-Themen 8
A Generische Datentypen Java Basics - Anfänger-Themen 8
bluerob generische ArrayList -> erbende Objekte auslesen Java Basics - Anfänger-Themen 24
L Generische Containerklasse Java Basics - Anfänger-Themen 9
V Methoden Umwandlung in generische Methode Java Basics - Anfänger-Themen 8
A Generische Collections und Vererbung Java Basics - Anfänger-Themen 2
S Generische HashMap Java Basics - Anfänger-Themen 2
M Klassen Generische Klassen, Personen und Gruppen Java Basics - Anfänger-Themen 6
L Generische ArrayList, CastProblem Java Basics - Anfänger-Themen 2
W generische Module Java Basics - Anfänger-Themen 2
S Generics und "generische Feldzuweisungen" Java Basics - Anfänger-Themen 5
C unterschied generische typen und supertypen als methodenparameter Java Basics - Anfänger-Themen 3
D Datentypen Generische Collections und Warnings Java Basics - Anfänger-Themen 8
F Generische Methoden Problem Java Basics - Anfänger-Themen 5
K Generische Klasse mit innerer Klasse | Problem mit Array Java Basics - Anfänger-Themen 6
B Was passiert, wenn eine konkrete Klasse von generische Klasse erbt? Java Basics - Anfänger-Themen 14
B Generische Vererbung was ist der Unterschied? Java Basics - Anfänger-Themen 4
W Generische Klassen Java Basics - Anfänger-Themen 3
W Generische Klassen und Casting Java Basics - Anfänger-Themen 6
F Generische Typen auch für statische Methoden? Java Basics - Anfänger-Themen 13
J array über generische arraylist Java Basics - Anfänger-Themen 7
B instanceof Prüfung für generische Typen Java Basics - Anfänger-Themen 5
H Statische generische Methode Java Basics - Anfänger-Themen 2
G Frage zum Ungang mit Generische Datentypen Java Basics - Anfänger-Themen 4
D generische methode <T> void . Java Basics - Anfänger-Themen 9
J Generische Methoden Java Basics - Anfänger-Themen 6
G Doppelt verkettete, generische Liste Java Basics - Anfänger-Themen 11
S Parameterübergabe: Generische Klasse Java Basics - Anfänger-Themen 4
S generische Felder Java Basics - Anfänger-Themen 2
P Generische Klasse Java Basics - Anfänger-Themen 8
C Generische Klassen, das erste Mal. Java Basics - Anfänger-Themen 8
F Generische Methode - was bringt der Wildcard Operator? Java Basics - Anfänger-Themen 7
F Generische Liste von generischen Objekten. Java Basics - Anfänger-Themen 3
H generische Methoden Java Basics - Anfänger-Themen 5
N generische HashMap und Iterator Java Basics - Anfänger-Themen 2
H generische Bausteine, heterogene Datenstrukturen Java Basics - Anfänger-Themen 2
J generische klassen neue Instanz Java Basics - Anfänger-Themen 5
H Generische Klassen. Java Basics - Anfänger-Themen 16
M Länge eines Arrays als Variable speichern möglich? Java Basics - Anfänger-Themen 14
R Liste und Arrays Java Basics - Anfänger-Themen 12
N mehrdimensionale arrays Java Basics - Anfänger-Themen 12
E Arrays in einer ArrayList miteinander vergleichen Java Basics - Anfänger-Themen 12
Kingdako Wie löse ich eine Mathematische Formel mit Arrays und Schleifen? Java Basics - Anfänger-Themen 32
D Wie kann man in Java nach Arrays auf Duplikate prüfen Java Basics - Anfänger-Themen 12
S Hilfe bei Praktischen Aufgaben von Arrays Java Basics - Anfänger-Themen 39
T Objekte mit arrays erstellen Java Basics - Anfänger-Themen 6
M Problem bei verschachtelter for-Schleife bei zweidimensionalen Arrays Java Basics - Anfänger-Themen 3
J Mehrdimensionale Arrays Java Basics - Anfänger-Themen 2
pc pc pc pc pc letztes Element eines Arrays n Java Basics - Anfänger-Themen 3
M Arrays Java Basics - Anfänger-Themen 3
Ostkreuz Wert von Arrays summieren Java Basics - Anfänger-Themen 1
Ostkreuz Summieren von Arrays Java Basics - Anfänger-Themen 4
javaBoon86 Arrays 2 Dimension Werte ausgeben Java Basics - Anfänger-Themen 15
paulen1 Best Practice "Unchecked Assignment" Warnung beim erstellen eines 2D Arrays of Arraylists Java Basics - Anfänger-Themen 2
B bei 2 Arrays Anzahl gleicher Elemente vergleichen? Java Basics - Anfänger-Themen 49
JustAProgrammer Ein Dreieck mit Arrays erstellen Java Basics - Anfänger-Themen 2
TheSepp Nur Arrays ausgeben, die Werte zugewiesen haben. Java Basics - Anfänger-Themen 4
volcanos Addition -> List<Integer> mit Arrays.asList() versus List<Integer>ArrayList<>() Java Basics - Anfänger-Themen 14
ArrayList mit unbekannter Menge an Arrays die Arrays vergleichen Java Basics - Anfänger-Themen 9
D Arrays an replaceAll-Methode übergeben Java Basics - Anfänger-Themen 12
rosima26 Geordnete Arrays ausgeben Java Basics - Anfänger-Themen 31
D Inhalt eines Arrays ausgeben Java Basics - Anfänger-Themen 7
A Jedes zweite Element eines Arrays entfernen Java Basics - Anfänger-Themen 30
C Zwei Arrays addieren und ausgeben Java Basics - Anfänger-Themen 3
E Zinsrechnung mithilfe von Arrays Java Basics - Anfänger-Themen 12

Ähnliche Java Themen

Neue Themen


Oben