Aufgabenstellung: Ein vom Benutzer angegebener arithmetischer A

Werbung
Algorithmen und Datenstrukturen
Übung 2: Generische "binäre Suchbaumknoten"
1
Aufgaben
1.1 Gib den Quellcode zu einer Java-Anwendung an, die die Definition einer Klasse
für einen Knoten eines binären Suchbaums zur Aufnahme von Zeichenketten
umfaßt. Die zu dieser Anwendung zugehörige Klasse soll
- eine Java-Anwendung darstellen
- einen binären Suchbaum mit 20 zufällig erzeugten Zeichenketten erstellen
- den binären Suchbaum so durchlaufen, daß die in den Baumknoten gespeicherten Zeichenfolgen in
aufsteigend sortierter Reihenfolge ausgegeben werden können.
1.2 Übersetze den unter a) vorliegenden Quellcode und führe die daraus
resultierende .class-Datei aus.
2. Der unter 1. erstellte Baumknoten kann nur Daten des Typs String aufnehmen.
Verallgemeinere die Anwendung so, daß beliebige Typen (zumindest solche die die
Schnittstelle Comparable implementiert haben) in den Binärbaumknoten
aufgenommen werden können.
2.1 Mit diesen generellen Baumknoten für einen Binärbaum ermittle im Rahmen
einer Java-Anwendung Methoden für
- das Zusammenstellen von Binärbaumknoten zu einem binären Suchbaum
- die Ausgabe einer aufsteigend sortierte Folge der Dateninhalte, die in den zu einem binären
Suchbaum zusammengestellten Binärbaumknoten aufgenommen wurden
- die Ausgabe der um 90 Grad gedrehten Struktur des binären Baums
2.2 Weiterhin bestimme Methoden für
- das Ermitteln des kleinsten Datenwerts, der in den zum binären Suchbaum zusammengestellten
Baumknoten, abgelegt wurde
- das Ermitteln des größten Datenwerts, der in den zum binären Suchbaum zusammengestellten
Baumknoten, abgelegt wurde
3. Der unter 2. angegebene generische Binärbaumknoten besitzt einen
entscheidenden Nachteil, den eine Übersetzung mit Java 1.5 aufdeckt. Beim
Übersetzen mit Java 1.5 erhält man folgende Hinweise:
2
Note: BinaerBaumknoten.java uses unchecked or unsafe operations
Note: Recompile with –Xlint:unchecked for details
Führt man den letzten Hinweis aus, dann zeigt der Compiler das Problem an:
1
2
pr42000
vgl. BinaeBaumknoten.java in pr12362
1
Algorithmen und Datenstrukturen
Mit Java 1.5 wird das Typsystem auf generische Typen erweitert. Eine generische
Klasse, die statische Typsicherheit garantiert, muß jedes Auftreten des Typs Object
(im vorliegenden Fall des Referenzdatentyps Comparable) durch einen
Variablennamen ersetzen. Die Variable ist eine Typvariable, die für einen beliebigen
Typ steht. Dem Klassennamen wird zusätzlich im Rahmen der Klassendefinition - in
spitzen Klammern eingeschlossen - hinzugefügt, dass diese Klasse eine
Typvariable benutzt.
Bei der Definition der Schablone kann eingeschränkt werden, dass eine Typvariable
nur für bestimmte Typen ersetzt werden darf. Es kann vorgeschrieben werden, dass
der Typ eine bestimmte konkrete Schnittstelle implementieren muß.
Ersetze in der unter 2. angegebenen Lösung den Typ Comparable durch eine
Typvariable mit der Angabe, dass diese Variable für alle Typen steht, die Subtypen
der Schnittstelle Comparable sind.
3
// Elementarer Knoten eines binaeren Baums, der nicht ausgeglichen ist
class BinaerBaumknoten<T extends Comparable>
{
// Instanzvariable
protected BinaerBaumknoten<T> links;
protected BinaerBaumknoten<T> rechts;
public
T daten;
// linker Teilbaum
// rechter Teilbaum
// Dateninhalt der Knoten
// Konstruktor
public BinaerBaumknoten(T datenElement)
{
this(datenElement, null, null );
}
public BinaerBaumknoten(T datenElement,
BinaerBaumknoten<T> l,
BinaerBaumknoten<T> r)
{
daten
= datenElement;
links
= l;
rechts
= r;
}
public void insert (T x)
{
if (x.compareTo(daten) > 0)
// dann rechts
{
if (rechts == null) rechts = new BinaerBaumknoten<T>(x);
else rechts.insert(x);
}
else // sonst links
{
if (links == null) links = new BinaerBaumknoten<T> (x);
else links.insert(x);
}
}
public BinaerBaumknoten<T> getLinks()
{
return links;
}
public BinaerBaumknoten<T> getRechts()
{
return rechts;
}
}
3
vgl. GenericBinaerBaumknoten.java in pr12362
2
Algorithmen und Datenstrukturen
Die Lösung ist noch nicht perfekt. Eine abgeleitete Klasse, deren Basisklasse
Comparable implementiert, wird nicht akzeptiert, weil die Definition <T extends
Comparable<T>> verlangt, dass das Typargument selbst Comparable
implementiert.
5. Generische Methoden: Generische Typen sind nicht an einen objektorientierten
Kontext gebunden. In Java verläßt man den objektorientierten Kontext in statischen
Methoden. Statische Methoden sind nicht an ein Objekt gebunden und lassen sich
daher in Java definieren. Hierzu ist vor die Methodensignatur in spitzen Klammern
eine Liste der für statische Methoden benutzten Typvariablen anzugeben.
Bsp. : Generische Methoden der Klasse TestGenericBinaerBaumKnoten
4
public class TestGenericBinaerBaumKnoten
{
public static void main (String args[])
{
BinaerBaumknoten<Integer> baum = null;
/*
for (int i = 0; i < 20; i++) // 20 Zusfallsstrings speichern
{
String s = "Zufallszahl " + (int)(Math.random() * 100);
if (baum == null) baum = new BinaerBaumknoten(s);
}
print(baum); // Sortiert wieder ausdrucken
*/
for (int i = 0; i < 10; i++)
{
// Erzeuge eine Zahl zwischen 0 und 100
Integer r = new Integer((int)(Math.random()*100));
if (baum == null) baum = new BinaerBaumknoten<Integer>(r);
else baum.insert(r);
}
System.out.println("Inorder-Durchlauf");
print(baum);
System.out.println();
System.out.println("Baumdarstellung um 90 Grad versetzt");
ausgBinaerBaum(baum,0);
System.out.print("Kleinster Wert: ");
System.out.print(((Integer)(findeMin(baum))).intValue());
System.out.println();
System.out.print("Groesster Wert: ");
System.out.print(((Integer)(findeMax(baum))).intValue());
System.out.println();
}
// Generische Methoden
public static <T extends Comparable>
void print (BinaerBaumknoten<? extends T> baum)
// Rekursive Druckfunktion
{
if (baum == null) return;
print(baum.getLinks());
System.out.print(baum.daten + " ");
print(baum.getRechts());
}
public static <T extends Comparable>
void ausgBinaerBaum(BinaerBaumknoten<T> b, int stufe)
{
if (b != null)
{
ausgBinaerBaum(b.getRechts(), stufe + 1);
for (int i = 0; i < stufe; i++)
{
4
vgl. pr12362
3
Algorithmen und Datenstrukturen
System.out.print("
");
}
System.out.println(b.daten);
ausgBinaerBaum(b.getLinks(), stufe + 1);
}
}
public static <T extends Comparable> T findeMin(BinaerBaumknoten<T> b)
{
return datenZugriff( findMin(b) );
}
public static <T extends Comparable> T findeMax(BinaerBaumknoten<T> b)
{
return datenZugriff( findMax(b) );
}
public static <T extends Comparable> T datenZugriff(BinaerBaumknoten<T> b)
{
return b == null ? null : b.daten;
}
public static <T extends Comparable>
BinaerBaumknoten<T> findMin(BinaerBaumknoten<T> b)
{
if (b == null) return null;
else if (b.getLinks() == null) return b;
return findMin(b.getLinks());
}
public static <T extends Comparable>
BinaerBaumknoten<T> findMax(BinaerBaumknoten<T> b)
{
if (b != null)
while (b.getRechts() != null)
b = b.getRechts();
return b;
}
}
6. Generische Typen können ein unbestimmtes Typargument benennen. Syntaktisch
wird dann als Typargument das Wildcardzeichen '?' angegeben, z.B.
BinaerBaumKnoten<?> b; . Ein derartiger generischer Typ heißt Wildcardtyp.
Alle gemischten Typen der gleichen generischen Klasse sind kompatibel zu diesem
Wildcardtyp. Zu einem Widcardtyp mit Typargument ? sind alle generischen Typen
der betreffenden generischen Klasse kompatibel. Wenn dieser Spielraum zu groß
ist, können Wildcarddarstellungen mit Typebounds eingeschränkt werden, z.B.
BinaerBaumknoten<? extends Number> nb; . Zu diesem eingeschränkten
Wildcardtyp sind nur noch die generischen BinaerBaumknoten-Typen kompatibel,
deren Typargument Number ist, z.B.
nb = new BinaerBaumKnoten<Integer>(23);
nb = new BinaerBaumKnoten<Object>(new Object());
// OK
// Fehler
Ein derartiger Typebound wird auch als "Upper-Typebound" bezeichnet, weil er aus
Sicht der Vererbungshierarchie eine "Obergrenze" für die konkreten Typargumente
festlegt. Ein mit extends eingeschränkter Wildcardtyp wird als covarianter
Wildcardtyp bezeichnet.
Der covariante Wildcardtyp BinaerBaumknoten<? extendsNumber> legt
lediglich fest, daß die Knoteninformation irgendeinen zu Number kompatiblen Typ
hat. Ein beliebiger anderer, ebenfalls zu Number kompatibler Typ muß dazu nicht
unbedingt passen. Der Schreibzugriff wird deshalb abgewiesen. Einzige Ausnahme
ist die Zuweisung des Werts null, die zu jedem Referenztyp kompatibel ist.
Lesende Zugriffe sind dagegen bei covarianten Wildcardtypen zulässig.
4
Algorithmen und Datenstrukturen
Alle diejenigen generischen Typen, deren Typargument vom Upper-Typbound
abgeleitet ist, sind zum Wildcardtyp kompatibel. Ebenso sinnvoll ist die umgekehrte
Einschränkung mit einem Lower-Typebound, z.B. BinaerBaumknoten<? super
Number> nb; . Die Typargumente müssen hier Basistypen des Lower-Typebound
sein.
Bsp.: BinaerBaumknoten <? super Number> nb;
nb = new BinaerBaumknoten<Object>(new Object());
nb = new BinaerBaumknoten<Integer>(23);
// OK
// Fehler
Die Situation wird als Contravarianz bezeichnet, die entsprechenden Wildcardtypen
als contravariante Wildcardtypen.
Der generische Typ BinaerBaumknoten<Object> ist bspw. kompatibel zum
kontravarianten Wildcardtyp BinaerBaumknoten<? super Object>, weil
Object ein Basistyp von Number ist.
Der kontravariante Wildcardtyp BinaerBaumknoten<?
super
Number>
garantiert, dass die Knoteninformation den Typ Number oder ein Basistyp davon hat.
Ein Number-Objekt ist dazu sicherlich kompatibel. Der Schreibzugriff ist deshalb
erlaubt.
Es kann aber nicht sichergestellt werden, dass die Knoteninformation wirklich
Number ist. Deshalb ist kein Lesezugriff zulässig. Eine Ausnahme ist das Lesen
eines Object, zu dem jeder Wildcardtyp kompatibel ist.
Mit kontraviantern Typebounds kann das Problem der generischen Knotenklasse,
z.B. class BinaerBaumKnoten<T extends Comparable<T>> gelöst werden,
deren Typebound im ersten Ansatz zu streng war und nur Typargumente zuließ, die
selbst das Interface Comparable implementieren. Akzeptiert werden sollen aber
auch Typargumente, bei denen irgendeine Basisklasse das Interface Comparable
implementiert: class BinaerBaumknoten<T extends Comparable<? super
T> .
5
5
Dieser Art von Typebound befindet sich bspw. auch im Java-API der Klasse Collections.
5
Herunterladen