# Generische Arrays durch Typecast mit Object-Array



## Eikonium (28. Apr 2010)

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:


```
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):

```
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 (28. Apr 2010)

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 
	
	
	
	





```
get
```
ter:
	
	
	
	





```
public E getIndex(int index) {
		return (E) array[index];
	}
```

 Tatsächlich ein generisches Array erzeugen (Code aus dem Link aus dem Post von oben):

```
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?


----------



## gman (28. Apr 2010)

Studierst du mit dem hier zusammen? Die Aufgabe/Probleme klingen irgendwie ähnlich


----------



## Landei (28. Apr 2010)

kay73 hat gesagt.:


> 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":


```
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.


----------



## Wortraum (28. Apr 2010)

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:

```
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


----------



## Landei (28. Apr 2010)

Na ja, es gibt schon einen kleinen Vorteil bei meiner Variante:

```
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
```


----------



## Eikonium (29. Apr 2010)

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:

```
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 (29. Apr 2010)

Ü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 (29. Apr 2010)

Landei hat gesagt.:


> 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!

```
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 */
```


----------



## nixnick (29. Apr 2010)

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 (30. Apr 2010)

Wortraum hat gesagt.:


> Ich behaupte erst einmal, daß Deine Variante gar nicht funktioniert. :noe: Zeile 14 ist der Bösewicht!
> 
> ```
> ArrayDictionary<Number> dict = new ArrayDictionary<Number>();
> ...



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!


----------



## Wortraum (30. Apr 2010)

Landei hat gesagt.:


> Stimmt, also muss man doch das Klassen-Objekt im Konstruktor übergeben, um den richtigen Array-Typ zu erhalten.


Schade eigentlich, ich fand Deine Idee nämlich gar nicht schlecht.


----------

