Einfache Klassen und Basistypen Inhaltsverzeichnis Hypertext-Version Implementierung von Multibehältern © APSIS GmbH

8. Multibehälter

Seite 171

Abb. 8.1: Datenbehälter für Buchstaben, für Telefonbucheinträge und für Messwerte

8.1. Mengen

Seite 172

Abb. 8.2: Mengenoperationen

8.1.1. Farbmengen

Seite 172

import lehrbuch.Farbe; // (8.1)
public interface Farbmenge { // alle final-Parameter sind auch const
	public void entleeren(); // löscht alle Farben aus der Farbmenge
	public void fuellen(final Farbe farbe); // trägt farbe die Farbmenge ein
	public void entfernen(final Farbe farbe); // löscht Farbe aus der Farbmenge
	public boolean vorhanden(final Farbe farbe); // const ¬
}
import lehrbuch.kapitel8.Farbmenge; // Schnittstelle // (8.2)
import lehrbuch.kapitel8.FarbmengeImpl; // Implementierung
import lehrbuch.Farbe;
public class ImmerGruen {
	public static void main(String args[]) {
		Farbmenge menge = new FarbmengeImpl(); // ¬
		menge.fuellen(Farbe.ROT); // ¬
		menge.fuellen(Farbe.ROT); // keine Veränderung
		menge.fuellen(Farbe.GRUEN);
		menge.entfernen(Farbe.ROT);
		menge.entfernen(Farbe.ROT); // keine Veränderung ¬
		boolean gruenIstDa = menge.vorhanden(Farbe.GRUEN); // true
	}
}
menge.kopieren(andereMenge); // kopieren aus FarbmengeImpl

Übung 8.1: Erstellen Sie ein menügesteuertes Programm für zwei Farbmengen mit Vorwahl (s. Übung 6.8 auf Seite 133). Generieren Sie mit Hilfe des Menügenerators das Menü, in das die einzelnen Operationsaufrufe eingebunden werden: "Erste Menge", "Zweite Menge" (Vorwahl), "Fuellen" (Auswahl der Farbe), "Entfernen" (mit Auswahl einer Farbe), "Entleeren" und "Vorhanden" (Auswahl einer Farbe, Ergebnis über meldung).

8.1.2. Erweiterung von Multibehältern

Seite 173

public interface ErwFarbmenge extends Farbmenge { // (8.3)
	public void und(final ErwFarbmenge menge); // const menge; // Schnitt
	public void oder(final ErwFarbmenge menge); // const menge; // Vereinigung
	public void entweder(final ErwFarbmenge menge); // const menge;
	public void nicht(); // const menge; // Komplement
	public boolean leer(); // const
	public void kopieren(final ErwFarbmenge quelle) throws VollAusn;
		// const quelle
	public boolean gleich(final ErwFarbmenge menge); // const menge; const
}

Abb. 8.3: Erweiterung und Implementierung

Übung 8.2: Ergänzen Sie Ihre Lösung der Übung 8.1 auf Seite 173 mit Menüpunkten, in denen Sie auch die Mengenoperationen testen. Benutzen Sie dabei die implementierende Klasse kapitel8.ErwFarbmengeImpl.

8.1.3. Zeichenmengen

Seite 174

public interface Zeichenmenge { // (8.4)
	public void entleeren(); // löscht alle Zeichen aus Zeichenmenge
	public void fuellen(final lehrbuch.Char zeichen); // trägt zeichen ein
	public void entfernen(final lehrbuch.Char zeichen); // löscht zeichen
	public boolean vorhanden(final lehrbuch.Char zeichen);
	public void allesAnzeigen(); // const // zeigt die gespeicherten Zeichen an
}

Übung 8.3: Erstellen Sie ein menügesteuertes Programm (wie in der Übung 8.1 auf Seite 173) für zwei Zeichenmengen mit Vorwahl.

Übung 8.4: Erweitern Sie die obige Schnittstelle Zeichenmenge um die mengentheoretischen Operationen Vereinigung, Schnitt und Komplement. Ergänzen Sie auch Ihr menügesteuertes Programm aus der Übung 8.3 durch Menüpunkte, mit deren Hilfe Sie die Vereinigung und den Schnitt Ihrer beiden Zeichenmengen sowie das Komplement von einer der beiden berechnen können. In die Implementierung müssen Sie zunächst einmal leere Methoden, d.h. Attrappen (dummies) einsetzen, da wir es noch nicht gelernt haben, wie Multibehälter implementiert werden.

8.1.4. Persistenz

Seite 175

public interface PersZeichenmenge extends Zeichenmenge { // (8.5)
	public void speichern(String dateiname) throws DateiAusn; // const ¬
		// Datei wird überschrieben, Objekt bleibt unverändert
	public void laden (final String dateiname) throws DateiAusn; // ¬
		// Objekt wird überschrieben, Datei bleibt unverändert
}

Übung 8.5: Beweisen Sie mit Hilfe eines Stapeltestprogramms (also kein Dialogprogramm), dass die Klasse kapitel8.PersZeichenmengeImpl persistent ist: Versorgen Sie zuerst Ihre Menge mit Inhalt und speichern Sie sie; anschließend erzeugen Sie ein neues Mengenobjekt, laden Sie sie und überprüfen Sie seinen Inhalt.

8.1.5. Generische Mengen

Seite 175

public interface Menge { // (8.6)
	public void entleeren(); // löscht alle Elemente aus der Menge
	public void fuellen(final Object element);
	public void entfernen(final Object element);
	public boolean vorhanden(final Object element); // const
	public boolean leer(); // const
	public void und(final Menge menge); // Schnitt
	public void oder(final Menge menge); // Vereinigung
	// public void entweder(final Menge menge);
		// nur für diskrete Mengen implementierbar, s. Kapitel 8.1.7.
	// public void nein(); // Komplement nur für diskrete Mengen implementierbar
	// public boolean voll(); // const // nur für diskrete Mengen implementierbar
	// public void kopieren(final Menge quelle) throws VollAusn;
	/* die Vergleichs- und Persistenzmethoden nehmen wir nicht in die Schnittstelle auf,
		damit sie nicht in jeder Implementierung enthalten sein müssen;
		das Kommentar empfiehlt, sie auch zu implementieren */
	// public boolean gleich(final Menge menge); // const
	// public void speichern(String dateiname) throws DateiAusn; // const
	// public void laden (final String dateiname) throws DateiAusn;
}
public MengeGen(final Object reg); // Konstruktor mit Registrierungsobjekt
public MengeGen(final MengeGen quelle); // Kopierkonstruktor
MengeGen farbmenge = new MengeGen(Farbe.GRUEN); // Registrierungsobjekt // (8.7)
MengeGen eimermenge = new MengeGen(new Eimer()); // anonymes Registrierungsobjekt
Farbe farbe = Farbe.ROT;
farbmenge.fuellen(farbe);
farbmenge.fuellen(Farbe.GRUEN); // anonymes Objekt wird in die Menge eingetragen
Eimer eimer = new Eimer();
eimer.fuellen();
eimermenge.fuellen(eimer);
farbmenge.fuellen(eimer); // throws GenFehler

8.1.6. Polymorphe Mengen

Seite 177

class MengeGen implements Menge { // (8.8)
	public MengeGen(final Object element) { ... } // merkt Klasse von element
	public MengeGen(final MengeGen menge) { ... } // like menge
	public void fuellen(final Object element) { ... } // like element
	// überprüft, ob der Parameter element von der Klasse ist wie der vom Konstruktor
	... // die Implementierung der weiteren Methoden aus Menge
}
class MengePol implements Menge { // (8.9)
	public MengePol() { ... }
	public MengePol(final MengePol menge) { ... }
	public void fuellen(final Object element) { ... } // keine Überprüfung
	... // die Implementierung der weiteren Methoden aus Menge
}

8.1.7. Diskrete Mengen

Seite 178

public interface DiskreteMenge { // alle final-Parameter sind auch const // (8.10)
	public void fuellen(final Aufz element); // like element
		... // usw. wie im Programm (8.6); zusätzlich noch das Komplement:
	public void nein(); // Komplement, für diskrete Mengen implementierbar
}
public void fuellen(final Object element);
public void fuellen(final Aufz element);
class Geschlecht extends lehrbuch.Aufz { // (8.11)
	public static final Geschlecht MAENNLICH = new Geschlecht();
	public static final Geschlecht WEIBLICH = new Geschlecht();
	public static final Geschlecht SAECHLICH = new Geschlecht();
}
DiskreteMenge eimermenge = new DiskreteMengeGen(new lehrbuch.Eimer()); // Fehler
	// wird vom Compiler abgelehnt, da Eimer nicht Unterklasse von Aufz
DiskreteMenge geschlechtmenge = new DiskreteMengeGen(Geschlecht.MAENNLICH);
geschlechtmenge.fuellen(Farbe.ROT); // throws GenFehler

8.1.8. Iteratoren

Seite 179

public interface AnzeigbareMenge extends Menge { // (8.12)
	public void allesAnzeigen(); // const // zeigt die gespeicherten Elemente an
}
public interface IterierbareMenge extends Menge { // (8.13)
	public void iterator(String rueckruf); // ruft rueckruf für jedes Element auf
}
public class Tier extends lehrbuch.Aufz { // (8.14)
	public static final Tier ELEFANT = new Tier();
		... // alle Tiere ähnlich
	public void tierAnzeigen() { // zeigt ein Tierfoto am Bildschirm an ¬
		System.out.println(text()); // primitive Version
	}
}
import lehrbuch.kapitel8.IterierbareMengeImpl;
public class Safari {
	public static void main(String args[]) {
		Tier geschossenesTier = Tier.ELEFANT;
		IterierbareMengeImpl menge = new IterierbareMengeImpl(geschossenesTier);
		geschossenesTier = (Tier)geschossenesTier.auswahl();
			// oder geschossenesTier anders bestimmen
		menge.fuellen(geschossenesTier);
		... // weitere Tiere eintragen
		menge.iterator("tierAnzeigen"); // ruft für jedes Tier tierAnzeigen auf
	}
}

Übung 8.6: Prägen Sie die Klasse kapitel8.MengeGen für Zeichen (d.h. für die Klasse lehrbuch.Char) aus. Benutzen Sie die Ausprägung an Stelle von ZeichenmengeImpl in Ihrem menügesteuerten Programm aus der Übung 8.3 auf Seite 175.

8.2. Säcke

Seite 180

public interface Zeichensack { // alle final-Parameter sind auch const // (8.15)
	public void entleeren(); // löscht alle Zeichen aus dem Zeichensack
	public void fuellen(final lehrbuch.Char zeichen);
	public void entfernen(final lehrbuch.Char zeichen) throws KeinEintragAusn;
	public void alleEntfernen(final lehrbuch.Char zeichen);
	public boolean vorhanden(final lehrbuch.Char zeichen);
	public boolean leer(); // const
	public void allesAnzeigen(); // zeigt jedes eingetragene Zeichen des Sackes an
	// public void kopieren(final Zeichensack quelle) throws VollAusn;
	// public boolean gleich(final Zeichensack sack); // const
	// public void speichern(String dateiname) throws DateiAusn; // const
	// public void laden (final String dateiname) throws DateiAusn;
	// public void iterator(String rueckruf);
}

Übung 8.7: Verwenden Sie die Klasse kapitel8.ZeichensackImpl (sie implementiert Zeichensack) in Ihrem Programm aus der Übung 8.3 auf Seite 175 an Stelle von kapitel8. ZeichenmengeImpl. Wenn ein Zeichen mehrfach mit fuellen (durch eine geeignete Menüauswahl) eingetragen und eins davon mit entfernen gelöscht wird, meldet vorhanden immer noch true. Der Aufruf der Operation allesAnzeigen beweist auch, dass mehrfach eingetragene Zeichen mehrfach vorhanden sind.

Übung 8.8: Fertigen Sie die allgemeine Version Sack der obigen Schnittstelle an und erweitern Sie sie zur Schnittstelle SackAlgebra, indem Sie Operationen hinzufügen, mit denen die Summe und die Differenz zweier Säcke errechnet werden kann. Prägen Sie eine Implementierung (mit Attrappen, d.h. leeren Methoden) Ihrer Erweiterung für Farben in einem Testprogramm aus.

8.3. Folgen

Seite 181

8.3.1. Zeichenfolgen

Seite 181

public interface Zeichenfolge { // (8.16)
	public void entleeren(); // löscht den gesamten Inhalt
	public void fuellen(final char zeichen); // trägt element ein
	public void entfernen(int index) throws LeerAusn; // löscht mit Index ¬
	public char lesen(int n) throws LeerAusn; // liefert das Zeichen mit Index ¬
	public boolean leer(); // const
	... Iterator, Persistenzoperationen, Kopieren und Gleichheit als Kommentar

Übung 8.9: Entwickeln Sie die allgemeine Schnittstelle Folge mit allen üblichen Multibehälter-Operationen. Nehmen Sie dazu auch die Operation zusammenfuegen (oder konkatenieren) und evtl. noch einige von Ihnen ausgedachte (z.B. invertieren, d.h. die Reihenfolge der Elemente umkehren) dazu.

8.3.2. Listen

Seite 182

Abb. 8.4: Folgen mit eingeschränktem Zugriff

8.3.3. Warteschlangen

Seite 182

public interface Warteschlange { // alle final-Parameter sind auch const // (8.17)
	public void entleeren(); // ensures leer()
	public void eintragen(Object element) throws VollAusn; // like element
		// requires !voll(); ensures !leer()
	public Object lesen() throws LeerAusn; // requires !leer()
	public void entfernen() throws LeerAusn; // like element
		// requires !leer(); ensures !voll()
	public boolean leer(); // const
	public boolean voll(); // const
	// public void kopieren(final Warteschlange quelle) throws VollAusn; // like quelle;
		// ensures gleich(quelle)
	// public boolean gleich(final Warteschlange warteschlange); // const
		// like warteschlange
	// public void speichern(String dateiname) throws DateiAusn; // const
	// public void laden(final String dateiname) throws DateiAusn;
	// diesmal kein iterator, da nur der älteste Eintrag sichtbar ¬
}
farbenschlange.eintragen(Farbe.ROT); // aufwärtskompatibel
Farbe farbe = (Farbe)farbenschlange.lesen();

Übung 8.10: Prägen Sie eine generische Implementierung dieser Schnittstelle (z.B. die Klasse lehrbuch.kapitel8.WarteschlangeListe), für eine Aufzählungsklasse Ihrer Wahl (wie z.B. lehrbuch.Farbe) aus, und rufen Sie ihre Operationen menügesteuert auf. Fangen Sie dabei die exportierten Ausnahmen auf, indem Sie den Anwender über ein Meldungsfenster über die fehlgeschlagene Aktion informieren.

8.3.4. Verwendung von Multibehältern

Seite 184

class Patient extends lehrbuch.Aufz { // (8.18)
	public static final Patient MAYER = new Patient();
	public static final Patient MUELLER = new Patient();
		... // alle Patienten werden aufgelistet
}
public class Arztpraxis extends MenueArzt { // generiert mit zwei Menüpunkten
	private lehrbuch.kapitel8.Warteschlange liste =
		new lehrbuch.kapitel8.WarteschlangeGen(Patient.MAYER, 20);
	protected void patientKommt() { // Rückrufprozedur für den ersten Menüpunkt
		try {
			lehrbuch.Aufz patient = Patient.MAYER;
			patient = patient.eingabe(); // throws BereichAusn;
			// keine Typkonvertierung nötig, weil patient vom Typ Aufz vereinbart wurde
			liste.eintragen(patient); // throws VollAusn; // ¬
		}
		catch(lehrbuch.BereichAusn ausnahme) {
			lehrbuch.Programm.meldung("Patient unbekannt", "Tippfehler");
		}
		catch(lehrbuch.kapitel8.VollAusn ausnahme) {
			lehrbuch.Programm.meldung("Draußen warten", "Wartezimmer voll");
		}
	}
	protected void dieNaechsteBitte() { // Rückruf für den zweiten Menüpunkt
		try {
			lehrbuch.Aufz patient = liste.lesen(); // throws LeerAusn;
			// keine Typkonvertierung nötig, weil patient vom Typ Aufz vereinbart wurde
			patient.meldung("Patient ausrufen");
			liste.entfernen(); // Patient wird aus der Liste ausgetragen ¬
		}
		catch(lehrbuch.kapitel8.LeerAusn ausnahme) {
			lehrbuch.Programm.meldung("Feierabend"); // kein Patient wartet
		}
	}
} // menue wird nun mit patientKommt und dieNaechsteBitte von start aufgerufen

Übung 8.11: Entwerfen und implementieren Sie auf diese Weise das menügesteuerte Programm Nikolaus, das wartenden Kindern (aus einem Objekt der Klasse kapitel8.WarteschlangeGen) Geschenke aus seinem Sack (kapitel8.SackGen) verteilt. Die Namen der Kinder und die Geschenke listen Sie im Programm als Werte einer Aufzählungsklasse auf. Der Anwender des Programms soll wählen, wann der Nikolaus ein Geschenk einkauft (und es in sein Sack legt), wann ein Kind bei ihm ankommt (und sich in die Warteschlange einreiht) und wann er das am längsten wartende Kind beschenkt. Das Geschenk soll über eine Auswahlliste ausgesucht werden; wenn der Nikolaus es nicht (mehr) in seinem Sack hat, muss er es über eine geeignete Meldung ablehnen.

8.3.5. Stapel

Seite 185

public interface Stapel { // (8.19)
	public void entleeren(); // ensures leer();
	public void eintragen(Object element) throws VollAusn;
		// requires !voll(); ensures !leer() &	lesen() == element
	public Object lesen() throws LeerAusn; // requires !leer()
	public void entfernen() throws LeerAusn;
		// requires !leer(); ensures !voll();
	public boolean leer(); // const
	public boolean voll(); // const
	// public void kopieren(final Stapel quelle) throws VollAusn; // like quelle
		// ensures gleich(quelle);
	// public boolean gleich(final Stapel stapel); // like stapel const
	// public void speichern(String dateiname) throws DateiAusn; // const
	// public void laden(final String dateiname) throws DateiAusn;
	// public void allesEntleeren(String rueckruf); // Anstelle des Iterators
		// ruft rueckruf für alle Elemente auf und löscht diese
}
StapelGen eimerStapel = new StapelGen(new Eimer());
package java.util; // (8.20)
public class Stack extends Vector { // s. Kapitel 9.3.9.
	public Stack();
	public void push(Object item); // entspricht eintragen
	public Object pop(); // ¬
	public Object peek(); // entspricht lesen ¬
	public boolean empty(); // entspricht leer
	public int search(Object object); // ¬
}

Übung 8.12: Gestalten Sie Ihr Programm aus der Übung 8.10 auf Seite 184 (FIFO-Farbbehälter) in einen LIFO-Farbbehälter um, indem Sie die generische Klasse kapitel8.StapelGen mit Farbe ausprägen. Zeigen Sie den Unterschied zwischen einer Warteschlange und einem Stapel.

8.3.6. Positionierbare Listen

Seite 187

public interface PosListe { // alle final-Parameter sind auch const // (8.21)
	public void entleeren (); // ensures leer();
	public void eintragen (Object element) throws VollAusn;
		// requires !voll(); ensures !leer() & aktuellesElement()==element;
		// trägt Element nach dem Element an der aktuellen Position in die Liste ein
	public void erstesEintragen(final Object element) throws VollAusn;
		// requires !voll(); ensures !leer() & aktuellesElement()==element;
		// trägt Element an die erste Position in die Liste ein
	public Object aktuellesElement() throws LeerAusn; // requires !leer();
		// liefert das Element an der aktuellen Position der Liste
	public void loeschen() throws LeerAusn; // requires !leer();
		// löscht das aktuelle Element aus Liste
	public void anfang() throws LeerAusn; // positioniert auf das erste Element
	public void ende() throws LeerAusn; // positioniert auf das letzte Element der Liste
	public void vorwaerts() throws LeerAusn; // navigiert eine Position nach vorne
	public void rueckwaerts() throws LeerAusn; // navigiert eine Position zurück
	public void suchen(final Object element) throws NichtGefundenAusn;
		// ensures aktuellesElement == element;
		// positioniert auf das nächste Vorkommnis von element nach der aktuellen
	// ... Iterator, Persistenzoperationen, Kopieren und Gleichheit als Kommentar
}

Übung 8.13: Rufen Sie die Operationen der generischen positionierbaren Liste kapitel8.PosListeGen für Farben menügesteuert auf. Testen Sie die Wirkung der Navigationsoperationen.

8.3.7. Implementierung mit positionierbaren Listen

Seite 187

public class MengePol implements Menge { // Schnittstelle aus (8.6) // (8.22)
	private PosListePol liste;
	public MengePol() { // Konstruktor
		liste = new PosListePol(); // ¬
	} 	public MengePol(final MengePol quelle) {
		liste = new PosListePol(quelle.liste); // Kopierkonstruktor weitergereicht
	}
	public void entleeren() { liste.entleeren(); } // weitergereicht
	public void fuellen(final Object element) { // ¬
		try {
			liste.anfang();
			liste.suchen(element); // gefunden, keine Veränderung
		} catch (PosListePol.NichtGefundenAusn ausnahme) { // eintragen: ¬
			try {
				liste.eintragen(element); // throws VollAusn
			}
			catch (VollAusn voll) { System.err.println("Programmfehler"); }
			/* kommt hoffentlich nicht vor: Die Schnittstelle Menge sieht den Fall nicht vor,
			dass beim Eintragen die Menge voll wird; er ist sehr unwahrscheinlich, dass der
			Speicher erschöpft ist, zumal jedes Element nur einmal aufgenommen wird */
		}
	}
	public void entfernen(final Object element) {
		... // ähnlich: wenn gefunden, loeschen; ansonsten keine Veränderung
	public boolean vorhanden(final Object element) {
		... // ähnlich: wenn gefunden, true; ansonsten false
	public void iterator(String rueckruf) { // ruft rueckruf für alle Elemente auf
		liste.iterator(rueckruf);
	}
	... leer, kopieren, gleich, speichern und laden ähnlich einfach weiterreichen
}

Übung 8.14: Implementieren Sie Stapel polymorph mit Hilfe der Klasse PosListePol.

8.3.8. Sequenzielle Dateien

Seite 188

W1 W2 ... Wi Wi+1 ... Wn Datei
      ­ ®     Position
        ­     Lesen
­             Zurücksetzen

Abb. 8.5: Sequenzielle Datei

public interface SeqDatei { // alle final-Parameter sind auch const // (8.23)
	public void neuBeschreiben(); // macht die Datei leer, bereit zum Beschreiben
	public void zuruecksetzen(); // macht die Datei bereit zum Lesen
	public void eintragen(final Object element) throws LesemodusFehler;
		/* trägt element an das Ende der Datei ein; nur im Schreibmodus; Ausnahme
		LesemodusFehler, wenn neuBeschreiben gar nicht oder nicht nach zuruecksetzen
		aufgerufen wurde */
	public void naechstesElement() throws SchreibmodusFehler, DateiendeAusn;
		/* schreitet zum nächsten Element; SchreibmodusFehler, wenn zuruecksetzen gar ¬
		nicht oder nicht nach neuBeschreiben aufgerufen wurde; DateiendeAusn, falls
		naechstesElement öfter als eintragen aufgerufen wurde */
	public Object aktuellesElement() throws SchreibmodusFehler, DateiendeAusn; // const
		// liefert das Element an der aktuellen Position
	public boolean endeDerDatei() throws SchreibmodusFehler;
		// true wenn der Aufruf naechstesElement die DateiendeAusn auslösen würde
	// public void kopieren(final SeqDatei quelle) throws VollAusn;
	// public boolean gleich(final SeqDatei quelle);
	// diesmal kein Iterator, keine Persistenzoperationen
	// exportierte Ausnahmen:
	public class LesemodusFehler extends Error {}
	public class SchreibmodusFehler extends Error {}
	public class DateiendeAusn extends Exception {}
}

Übung 8.15: Implementieren Sie die Schnittstelle SeqDatei mit Hilfe der generischen Klasse PosListeGen.

8.3.9. Sortierkanäle

Seite 190

Abb. 8.6: Sortierkanal

public interface Geordnet { // (8.24)
	public boolean kleiner(Geordnet objekt); // vergleicht zwei Objekte
	// a.kleiner(b) && b.kleiner(c) impliziert a.kleiner(c)
}
public interface Sortierkanal { // (8.25)
	public void entleeren ();
	public void eintragen (final Geordnet element) throws VollAusn;
	public void entfernen() throws LeerAusn; // des kleinsten Elements
	public Geordnet kleinstesLesen() throws LeerAusn;
	public boolean voll(); // weiteres Eintragen nicht möglich
	public boolean leer();
	// Iterator, Persistenzoperationen, Kopieren und Gleichheit wie üblich als Kommentar
}
class GeordneteFarbe extends Aufz implements Geordnet { // (8.26)
	public static final GeordneteFarbe ROT = new GeordneteFarbe();
		... // Aufzählungswerte und Konstruktore wie üblich
	public boolean kleiner(Geordnet farbe) { // ¬
		return kleiner(farbe);
	}
}
	...
SortierkanalGen farbkanal = new SortierkanalGen(GeordneteFarbe.ROT);
GeordneteFarbe farbe = GeordneteFarbe.ROT;
farbkanal.eintragen(farbe);
	... // weitere Farben eintragen
GeordneteFarbe kleinstes = (GeordneteFarbe)farbkanal.kleinstesLesen();
class GeordneterEimer extends Eimer implements Geordnet {
	public boolean kleiner(Geordnet eimer) {
		boolean ergebnis;
		... // Algorithmus, der bestimmt, wie zwei Eimer miteinander verglichen werden
		 // (z.B. nach Inhalt oder Position)
		return ergebnis;
	}
}
GeordneterEimer eimer = new GeordneterEimer();
SortierkanalGen eimerKanal = new SortierkanalGen(eimer);
eimer.fuellen();
eimerKanal.eintragen(eimer);
eimer = (GeordneterEimer)eimerKanal.kleinstesLesen();
eimer.anzeigen();

8.4. Assoziativspeicher

Seite 192

Abb. 8.7: Assoziativspeicher

8.4.1. Allgemeine Assoziativspeicher

Seite 192

public interface AssoSpeicher { // (8.27)
	public void entleeren();
	public void eintragen(final Object schluessel, final Object element) // ¬
		throws VollAusn;
	public Object finden(final Object schluessel) throws NichtVorhandenAusn; // const ¬
	public boolean vorhanden(final Object schluessel); // const
	public boolean leer(); // const
	public boolean voll(); // const
	public class NichtVorhandenAusn extends Exception {}
	// Iterator, Persistenzoperationen, Kopieren und Gleichheit wie üblich als Kommentar
}
AssoSpeicherGen telefonbuch = new AssoSpeicherGen(telefonnummer, teilnehmer);
public interface AssoTab extends AssoSpeicher { // (8.28)
	public Object naechstesFinden() throws NichtVorhandenAusn;
}

Übung 8.16: Schreiben Sie einen Stapeltesttreiber für den Assoziativspeicher. Im Gegensatz zu einem Dialogtesttreiber ist dies ein konstantes Programm: Es läuft jedes Mal gleich ab. Sein Sinn ist, dass es für verschiedene Implementierungen der zu testenden Klasse (z.B. nach einer Fehlerkorrektur) ihre jeweilige Funktionalität überprüfen kann. Die Testdaten werden hier nicht interaktiv eingegeben, sondern sind im Programm festgelegt.

Prägen Sie nun den Assoziativspeicher mit den Klassen lehrbuch.Char und Buchstabe aus, wobei diese letztere eine Aufzählungsklasse mit den Buchstaben von A bis Z sein soll. Sie bildet den Schlüssel: Jedem seiner Werte wird der entsprechende Buchstabe der Klasse lehrbuch.Char zugeordnet. Ihr Programm soll 20 Eintragungen (in gemischter Reihenfolge) in den Assoziativspeicher vornehmen. Überprüfen Sie anschließend mit Ausgabe über System.out.println, ob alle 20 ordnungsgemäß eingetragen werden.

8.4.2. Direkte Dateien

Seite 193

public interface DirDatei { // alle final-Parameter sind auch const // (8.29)
	public void neuBeschreiben(); // macht die Datei leer, bereit zum Beschreiben
	public void zuruecksetzen(); // macht die Datei bereit zum Lesen
	public void eintragen(final Object element) throws LesemodusFehler;
		/* trägt element an das Ende der Datei ein; nur im Schreibmodus; schluessel kann
		abgefragt werden; Ausnahme LesemodusFehler, wenn neuBeschreiben gar nicht oder
		nicht nach zuruecksetzen aufgerufen wurde */
	public int schluessel(); // gibt den Schlüssel der aktuellen Position zurück
	public void positionieren(int schluessel) throws DateiendeAusn, SchreibmodusFehler; // ¬
	public void naechstesElement() throws SchreibmodusFehler, DateiendeAusn;
		/* schreitet zum nächsten Element; SchreibmodusFehler, wenn zuruecksetzen gar
		nicht oder nicht nach neuBeschreiben aufgerufen wurde; DateiendeAusn, falls
		naechstesElement öfter als eintragen aufgerufen wurde */
	public Object aktuellesElement() throws SchreibmodusFehler, DateiendeAusn; // const
		// liefert das Element an der aktuellen Position; nur im Lesemodus
	public boolean endeDerDatei() throws SchreibmodusFehler;
		// true wenn naechstesElement die DateiendeAusn auslöst; nur im Lesemodus
	// public void kopieren(final SeqDatei quelle) throws VollAusn;
	// public boolean gleich(final SeqDatei quelle);
	// diesmal kein Iterator, keine Persistenzoperationen
	public class LesemodusFehler extends Exception {}
	public class SchreibmodusFehler extends Exception {}
	public class DateiendeAusn extends Exception {}
}

Einfache Klassen und Basistypen Inhaltsverzeichnis Hypertext-Version Implementierung von Multibehältern © APSIS GmbH