3.3 Realisierung von ADTs in Java • Einführung in Java siehe Kap. 5 © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-117 3.3.1 Realisierung des ADT Stack in Java Datentyp: STACK parm ELEM benutzt { BOOL } create: ! STACK push: ELEM " STACK ! STACK empty?: STACK ! BOOL top: STACK ! ELEM pop: STACK ! STACK Gleichungen mit Variablen E # ELEM, S # STACK empty?(create) = true (S1) empty?(push(E, S)) = false (S2) top(create) = errorELEM (S3) top(push(E, S)) = E (S4) pop(create) = errorSTACK (S5) pop(push(E, S)) = S (S6) Ende Datentyp © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-118 Realisierung in SCHEME und Java • Realisierung des ADT Stack durch SCHEME-Listen: (define (create) '()) (define (push I S) (cons I S) ) (define (empty? S) (null? S) ) (define (top S) (if (empty? S) 'error-INT (car S)) ) (define (pop S) (if (empty? S) 'error-STACK (cdr S)) ) • Realisierung des ADT Stack in Java durch ein Array siehe Extra-Folien – Interface Stack – Klasse ArrayStack – Klasse und Testprogramm TestArrayStack © Klaus Hinrichs 3.3.2 Informatik I – Datentypen und Datenstrukturen 3-119 Speichermodell imperativer Sprachen • Betrachte Attribut / Variable als die Verbindung zwischen einem Namen und einem Speicherbereich, der in der Lage ist, Werte des jeweils passenden Typs aufzunehmen. • Beispiel: Zuweisungen und Seiteneffekte int x; … x = 5; … x = x + 1; … x = x + 1; … Erklärung des Seiteneffektes durch eine Zuweisung (Assignment) mit Speichermodell, das einem Namen einen lesbaren und schreibbaren Speicherplatz für Werte zuordnet. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-120 Name vs. Speicherbereich • Die Reihenfolge der Seiteneffekte bestimmt die jeweils aktuellen Bindungen der Namen an Werte bzw. die Belegung der Speicherzellen und damit die weitere Abarbeitung eines Programms (z.B. in Conditionals). ! Genaue Festlegung der Schrittreihenfolge durch Steuerbefehle wesentlich für jedes Programm (" imperative Programmiersprache). • Praktische Realisierung der Beziehung Name - Speicherbereich: Größe des Speicherbereichs? • In der Regel bekannt: Art der Werte, die mit einem Namen verbunden werden können, z.B. Typ eines Attributs oder einer Variablen. • Vorteil getypter Sprachen: viele Fehler können statisch, d.h. vor Ablauf bzw. ohne Ablauf des Programmes, erkannt werden. Nachteil ungetypter Sprachen: Erkennung von Fehlern erst zur Laufzeit (dynamisch). • Deklarationen – ermöglichen getypte, überprüfbare Programmierung. – liefern Information für Zuordnung von Speicherplatzgrößen zu Namen. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-121 Deklaration - Instanziierung - … • Deklaration ist analog zu Attributen eine Vereinbarung, für welche Art von Werten ein Bezeichner stehen soll; dabei gibt es zwei Fälle: – einfache Wertebereiche wie z.B. int " Deklaration selbst sorgt für Anlegen von hinreichend viel Speicherplatz für die passenden Werte. – komplexe Objekte (Instanzen von Klassen) " Deklaration sorgt für Anlegen von Speicherplatz für eine Referenz auf die passenden Werte, die oft so flexibel sind, daß ihre genaue Größe von Parametern etc. abhängen kann, die statisch noch nicht bekannt sind. • Instanziierung: – bei einfachen Werten mit Deklaration implizit miterledigt – bei Bezeichnern für komplexe Typen explizites Anlegen von Speicherplatz für die Instanz (Objekt) des vereinbarten Typs (Klasse) mittels new und Konstruktoren. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-122 … Initialisierung - Definition • Initialisierung: Instanzvariable mit initialen Werten belegen; dies sollte möglichst explizit in Konstruktoren gemacht werden Default-Werte sind immer gefährlich! Initialisierung = erste Definition = erste Zuweisung • Definition: Zuweisung eines neuen Wertes (Seiteneffekt) Grundprinzip imperativer Sprachen ist es, den Berechnungsfortschritt durch Schreiben von Speicherzellen festzuhalten; am deutlichsten wird das beim Resultatwert einer Methode, der explizit mittels return unter Angabe des Namens des Speicherplatzes, an dem das Ergebnis steht, übergeben wird, statt implizit wie z.B. in SCHEME. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-123 Unterschied zwischen einfachen Werten und Referenzen • Beispiel: Unterschied zwischen dem einfachen Datentyp int und der Klasse Integer in Java int: es wird Platz für den Wert angelegt. Integer: es wird Speicherplatz für eine Referenz angelegt, die bei der Initialisierung mit new dann mit einem Verweis auf den Speicherplatz für das eigentliche Integer-Objekt, also die Instanz, belegt wird. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-124 … Beispiel … // Deklaration von Instanz// variablen (Attributen) // Wertebereich int int i1,i2,i3; // Klasse Integer Integer io1,io2,io3,io4; varname … int i1 int i2 int i3 int i4 Integer Integer Integer Integer contents … 1 2 3 4 io1 io2 io3 io4 // Deklaration plus // Definition/Initialisierung … … int i4 = 4; // io1 = 4 waere ein Fehler bis Java 1.4, // ab Java 5.0 ist es jedoch erlaubt! Werte // Definitionen/Zuweisungen Referenzen i1 = 1; Garbage i2 = i1+1; i3 = i1+i2; © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-125 … Beispiel … // Instanziierungen mit Definition io1 = new Integer(1); io2 = new Integer(i3 - 1); io3 = new Integer(io2.intValue() + 1); io4 = new Integer(4); // Ab io1 = io2 = io3 = io4 = Java 5.0 mit Autoboxing: 1; i3 - 1; io2 + 1; 4; varname … int i1 int i2 int i3 int i4 Integer Integer Integer Integer … Werte Referenzen io1 io2 io3 io4 contents … 1 2 3 4 … 1 2 3 4 Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-126 … Beispiel … // Definitionen/Zuweisungen io4 = io2; // io4 Referenz wie io2 varname … int i1 int i2 int i3 int i4 Integer Integer Integer Integer … io1 io2 io3 io4 Werte Referenzen contents … 1 2 3 4 … 1 2 3 4 Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-127 … Beispiel … io2 = io1; // io2 Referenz wie io1 varname … int i1 int i2 int i3 int i4 Integer Integer Integer Integer … Werte Referenzen io1 io2 io3 io4 contents … 1 2 3 4 … 1 2 3 4 Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-128 … Beispiel … // i2 // // varname … Wechsel: Integer " int = io3.intValue(); i2 = io3 waere bis Java 1.4 ein Fehler // Ab Java 5.0 mit Autoboxing: i2 = io3; int i1 int i2 int i3 int i4 Integer Integer Integer Integer … io1 io2 io3 io4 Werte Referenzen contents … 1 2 3 3 4 … 1 2 3 4 Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-129 … Beispiel varname … // Wechsel: Int " Integer io2 = new Integer(i2); // Ab Java 5.0 mit Autoboxing: io2 = i2; int i1 int i2 int i3 int i4 Integer Integer Integer Integer … Werte Referenzen io1 io2 io3 io4 contents … 1 3 3 4 … 1 2 3 4 3 Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-130 Alias Effekte • Alias-Beziehung zwischen zwei Namen: Referenzen verschiedener Namen verweisen auf die gleiche Speicherzelle. • Beispiel: Alias Effekt bei Referenzen auf Arrays ... varname … int[] f1 int[] f2 int[] f3 … contents … … int[] f1, f2, f3; // Deklaration Werte Referenzen Garbage Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-131 … Beispiel … // Instanziierung (Anlegen) f1 = new int[4]; // Initialisieren // f1 " [100,99,98,97] f1[0] = 100; f1[1] = 99; f1[2] = 98; f1[3] = 97; varname … int[] f1 int[] f2 int[] f3 … 0 1 2 3 contents … … 100 99 98 97 Werte Referenzen Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-132 … Beispiel … // Instanziierung (Anlegen) f2 = new int[2]; // Initialisieren // f2 " [0,1] f2[0] = 0; f2[1] = 1; varname … int[] f1 int[] f2 int[] f3 … … 0 1 2 3 0 1 Werte contents … … 100 99 98 97 … 0 1 Referenzen Garbage Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-133 … Beispiel … f3 = f2; // gleiche Referenz: // Alias f3 = f2 " [0,1] varname … int[] f1 int[] f2 int[] f3 … … Werte 0 1 2 3 0 1 contents … … 100 99 98 97 … 0 1 Referenzen Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-134 … Beispiel … f3[0] = 99; f2[1] = 55; // f2 = f3 " [99,55] // f2[0] = 99; f3[1] = 55; // haette gleichen Effekt varname … int[] f1 int[] f2 int[] f3 … … 0 1 2 3 0 1 Werte contents … … 100 99 98 97 … 0 99 1 55 Referenzen Garbage Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-135 … Beispiel … f2 = new int[2]; // neue Referenz fuer f2 // durch neue Instanziierung f2[0] = 3; f2[1] = 4; // Initialisieren f2 " [3,4] varname … int[] f1 int[] f2 int[] f3 … … Werte … Referenzen 0 1 2 3 0 1 0 1 contents … … 100 99 98 97 … 99 55 … 3 4 Garbage © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-136 … Beispiel f3 // // // = f2; erneuter Alias f3 = f2 " [3,4] [99,55] jetzt Garbage ... varname … int[] f1 int[] f2 int[] f3 … … Werte … Referenzen 0 1 2 3 0 1 0 1 contents … … 100 99 98 97 … 99 55 … 3 4 Garbage © Klaus Hinrichs 3.3.3 • • • • • • Informatik I – Datentypen und Datenstrukturen 3-137 Fallstudie: Numbers Interface Num Klasse IntNum Klasse ComplNum Interface Order Klasse OrderIntNum Klasse und Testprogramm Test © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-138 3.4 Dynamische Datenstrukturen • Problem: Speicherung von Daten, deren Umfang bzw. Zahl beim Schreiben des Programms noch nicht feststeht. • Lösung: Dynamische Datenstrukturen • Eine Datenstruktur heißt dynamisch, wenn ihre Größe sich nicht fest vorhersagen bzw. nach oben beschränken läßt. • Beispiele für abstrakte Datentypen bzw. Wertebereiche, die bei einer Realisierung dynamische Datenstrukturen benötigen: – ADT LIST wegen beliebiger, endlicher Anzahl von cons-Operationen. – ADTs STACK bzw. QUEUE wegen beliebiger, endlicher Anzahl von push- bzw. enqueue-Operationen. – ADT BTREE. – Rekursiv definierte Wertebereiche der Bäume bzw. der Wörter W($) = $* über $. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-139 Erste Ideen • Idee: Ordne einer dynamischen Datenstruktur von vornherein die auf dem zur Verfügung stehenden Rechner maximal mögliche Größe zu. " Maximal mögliche Flexibilität für diese Datenstruktur, für weitere Datenstrukturen gibt es allerdings keinen Platz mehr. • Modifikation der Idee: Teile den vorhandenen Speicherplatz unter allen Datenstrukturen a priori auf. • Beispiel: Telefonbuch mit gleich vielen Seiten für jeden Anfangsbuchstaben, z.B. 10 Seiten ! Alle Namen mit Anfangsbuchstabe M können nicht untergebracht werden (wegen Meier, Müller, …), während für Q, X, Y jeweils nur eine Seite benötigt wird und die übrigen 9 Seiten nicht benutzt werden. Verteilung vorhersagbar oder bekannt ! Lege Anzahl Seiten nach dem erwarteten Bedarf fest. • Dies funktioniert jedoch meist nicht für dynamische Datenstrukturen! © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-140 Implementierung zweier Stacks in einem Array • Beispiel: Realisierung zweier Stacks s1 und s2, deren Auslastung (d. h. die Anzahl push-Operationen je STACK) man nicht vorher abschätzen kann. Annahme: Speicherplatz für 1000 Stackeinträge M [0 : 999] Möglichkeit 1: M [0 : 499] für s1 und M [500 : 999] für s2 Wachstum der Stacks Problem: s1 fast leer, s2 läuft über ! nicht optimale Speicherplatzausnutzung © Klaus Hinrichs M 999 998 … 502 501 500 499 498 … 2 1 0 s2 s1 Informatik I – Datentypen und Datenstrukturen 3-141 Implementierung zweier Stacks in einem Array • Beispiel (Fortsetzung): Optimale Möglichkeit 2: s1 beginnt bei M [0] und bei jedem push wird das Element mit nächst höherem Index belegt. s2 beginnt bei M [999] und bei jedem push wird das Element mit nächst niedrigerem Index belegt: Wachstum Vorteil: der Stacks Problem tritt erst ein, wenn wirklich keine Speicherzelle mehr verfügbar! Unabhängigkeit von der Verteilung der push-Operationen auf s1 oder s2. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen M 999 998 … 502 501 500 499 498 … 2 1 0 s2 s1 3-142 Realisierung von dynamischen Datenstrukturen in Java • SCHEME: Realisierung von dynamischen Datenstrukturen sehr einfach Problem: schwache Typunterstützung. • Java: – Wie kann man die Parameter von ADT-Spezifikationen flexibel halten? – Wie kann man die Anzahl der Elemente, d.h. die Größe der Datenstruktur, flexibel halten? • Betrachte LIST(ELEM). Idee: Element ist Objekt im Speicher, auf das über eine Referenz zugegriffen werden kann, d.h. der Name ist über eine Referenz mit einem Wert verbunden, nicht direkt mit dem Wert selbst. • Beispiel: Anzahl der benötigten Integer-Elemente bekannt ! – lege für jedes einen Namen fest, z.B. i1, i2, i3 – instanziiere Namen, z.B. i1 = new Integer(50) – deklariere weitere Namen nach Bedarf. • Problem: Neudeklaration zur Laufzeit nicht möglich! Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-143 Realisierung von Listen in Java • Umgehung des Problems: Bei der Speicherung eines Elementes deklariere und instanziiere den Platz für das evtl. als nächstes hinzukommende Element. • Realisierung durch Objekte, die neben einem Element eine Referenz auf ein möglicherweise notwendiges Objekt der gleichen Art speichern: e1 e2 e3 class ListElem { Elem e; ListElem next; ListElem(Elem elem) { e = elem; next = null; } } © Klaus Hinrichs e4 null • e und next sollte man private setzen und entsprechende getter/setter Methoden bereitstellen. • null: Konstante für eine nicht definierte Referenz Informatik I – Datentypen und Datenstrukturen 3-144 Operationen auf Listen … • Beispiel: Erzeugung einer Liste: class ListInt { int e; ListInt next; ListInt(int elem) { e = elem; next = null; } } tmp list 0 ListInt list = new ListInt(0); ListInt tmp = list; for (int i = 1; i < 4; i++) { tmp.next = new ListInt(i); tmp = tmp.next; } tmp tmp tmp tmp null 1 null 2 null 3 null Traversierung der Liste (Ausgabe): ListInt tmp = list; while (tmp != null) { System.out.print(tmp.e + " "); tmp = tmp.next; } Ausgabe: i 12 3 2 3 0 14 Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-145 … Operationen auf Listen … • Beispiel (Fortsetzung): Erweiterung der (nichtleeren!) Liste: ListInt tmp = list; while (tmp.next != null) tmp = tmp.next; tmp.next = new ListInt(4); list © Klaus Hinrichs tmp tmp tmp tmp 0 1 2 3 Informatik I – Datentypen und Datenstrukturen null 4 null 3-146 … Operationen auf Listen • Beispiel (Fortsetzung): Entfernen des letzten Elementes aus der (aus mehr als 1 Element bestehenden!) Liste: alternativ: ListInt pred = null; ListInt tmp = list; while (tmp.next != null) { pred = tmp; tmp = tmp.next; } pred.next = null; null pred © Klaus Hinrichs list ListInt tmp = list; while (tmp.next.next != null) tmp = tmp.next; tmp.next = null; tmp tmp tmp tmp tmp null 0 1 2 3 pred pred pred 4 null pred Informatik I – Datentypen und Datenstrukturen 3-147 Listenelemente vs. Liste • Unschön: Fehlende Unterscheidung zwischen einem einzelnen Listenelement und einer Liste, die aus vielen solchen aneinandergeketteten Listenelementen besteht. • Realisierung einer Liste durch eine Klasse, die neben der Kette der Listenelemente auch Zusatzinformationen wie die Länge enthält: class ListImpl implements List { int length; // Anzahl Elemente ListElem list; // die Kette ListImpl() { length = 0; list = null; } … cons, car, cdr, … } • interface List beschreibt den ADT LIST. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-148 Realisierung des ADT LIST • Problem: Entfernen des letzten Elementes aus der Liste Lösung durch 1. Möglichkeit: Zwei-Zeiger-Technik siehe vorhergehende Folie mit pred, tmp 2. Möglichkeit: Benutzung der Längeninformation length und Zähler • Bisherige Repräsentation zur Realisierung der typischen Operationen nicht geeignet: # cons und car erfordern Durchlauf der Liste ! Aufwand linear in der Anzahl Listenelemente. # Zugriff auf cdr ohne Zerstörung der Liste nur durch Kopieren möglich. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-149 Umgekehrt vorwärts-verkettete Liste … • Besser: Umgekehrt vorwärts-verkettete Liste # next verweist immer noch auf das nächste Element der Kette # list in ListImpl verweist auf das zuletzt eingefügte Element, dieses auf das vorletzte, etc. # cons, car und cdr (mit Löschen des zuletzt eingefügten Elementes als Seiteneffekt) in konstanter Zeit möglich this.list 4 3 2 1 0 null • Realisierung des cons: void cons(Elem e) { tmp = new ListElem(e); this.length = this.length + 1; tmp.next = this.list; this.list = tmp; } © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-150 … Umgekehrt vorwärts-verkettete Liste • Realisierung des cdr ohne löschenden Seiteneffekt: ListImpl cdr() { ListImpl result = new ListImpl(); result.length = this.length - 1; result.list = (this.list).next; return result; } Gefahr: Man hat jetzt zwei Listenobjekte, die - bis auf das erste Element der Originalliste - auf die gleiche Kette von Elementen verweisen: ListImpl l1, l2; l1.list l2.list … l2 = l1.cdr(); null 4 3 2 1 0 length-Information muß nicht mehr korrekt sein, wenn eine Liste nicht nur durch cons oder cdr, sondern auch durch direkte Manipulation eines next verändert wird. Verändert z.B. l2 die Liste, so wird l1.length nicht aktualisiert, sondern nur l2.length! Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-151 Weitere Listenoperationen … • Naive Realisierung der append-Methode: l1.append(l2) hängt Liste l2 an die aufrufende Liste l1 l1.list 6 5 4 l2.list 2 1 0 2 1 3 null null Problem: l1.append(l1) l1.list 3 0 null Listentraversierung mittels while und Test auf null funktioniert nicht mehr ! Endlosschleife!!! © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-152 … Weitere Listenoperationen Lösung: Im Falle von identischen l1 und l2 kopiere Liste vollständig, bevor kopierte Liste angehängt wird: l1.list 3 2 1 0 null 3 2 1 0 null Informatik I – Datentypen und Datenstrukturen © Klaus Hinrichs 3-153 Implementierung von Datenstrukturen durch Listen • Einfach verkettete Listen gut geeignet für alle Datenstrukturen, die an der gleichen Stelle einfügen und löschen, wie z.B. STACK und LIST. • Realisierung des ADT STACK in Java durch Listen siehe Extra-Folien - Interface Stack - Klasse ListNode - Klasse ListStack - Klasse und Testprogramm TestListStack • Implementierung des ADT QUEUE durch vorwärts verkettete Liste mit zwei Zeigern: list verweist auf das zuletzt eingefügte Element, front auf das zuerst eingefügte Element: 0 1 front Realisierung von dequeue: front = front.next; 2 3 4 null list Realisierung von enqueue: list.next = new ListElem(e); list = list.next; " konstanter Aufwand für alle QUEUE-Operationen! © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-154 Doppelt verkettete Listen • Einfach verkettete Listen nicht sinnvoll für beliebiges Navigieren " doppelt-verkettete Listen mit zwei Verweisen succ und pred: © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-155 Parameter von ADT Spezifikationen • Wie kann man die Parameter von ADT-Spezifikationen flexibel halten? • Erst ab Version Java 5.0 unterstützt Java parametrisierte Klassen. • In älteren Versionen (bis Java 1.4) kann man parametrisierte Klassen bis zu einem gewissen Grad mit Hilfe des polymorphen, klassenbasierten Typkonzepts simulieren. Für alle Klassen, die letztendlich von java.lang.Object abhängen, kann man beliebige Flexibilität erreichen, indem man Parameter als zur Klasse Object gehörig realisiert. Problem: Vorteil der sicheren Typisierung geht verloren, da fast alles unter diese Kategorie paßt. " Laufzeitfehler! Werte aus Basis-Wertebereiche müssen künstlich zu Objekten gemacht werden, z.B. int-Werte zu Objekten der Klasse Integer. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-156 Preis für Flexibilität • Flexibilität bedeutet oft nicht-wahlfreien Zugriff: Repräsentation ermöglicht nur deshalb dynamische Größe, da die interne Struktur nur sehr einfache Zugriffe (wie z.B. erstes Element vs. Rest der Liste) erlaubt. Das führt für beliebigen Zugriff zu relativ hohem Aufwand (Bsp. Suchen in einer Liste). Zusätzlich kostet jedes zu speichernde Element zusätzlichen Verwaltungsaufwand (z.B. nextVerweis usw.). • Starre Obergrenzen erlauben oft im voraus zu berechnenden wahlfreien Zugriff (Bsp. array), und der Verwaltungsoverhead bezieht sich nur auf die gesamte Struktur, nicht auf jedes zu speichernde Element. Andererseits wird oft Speicherplatz verschenkt, wenn die vorgesehene Größe bei weitem nicht ausgenutzt wird (Bsp: array). © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-157 Hybride Datenstrukturen • Hybride Datenstrukturen: – Liste von Arrays moderater Größe: statt einzelner Elemente werden Arrays verkettet, in deren Innerem wahlfreier Zugriff herrscht; d.h. linearer Zugriff bis zum richtigen Array (abhängig von der Zahl der Arrays), dann im zweiten Schritt wahlfreier Zugriff; Speicherplatzbedarf nur moderat über dem wirklich Benötigten – Zwei-Stufige Speicherung: Array mit Verweisen auf weitere Arrays, die immer erst bei Bedarf angelegt werden. Sind die Größen bekannt, kann jeder Zugriff wahlfrei erfolgen wie in einem mehrdimensionalen Array. – Relozierbare Arrays: Anlegen eines ersten Arrays und Nutzung; reicht das nicht mehr aus, wird ein weiteres doppelt so großes Array angelegt, wobei das erste Array in die erste Hälfte des neuen Arrays kopiert wird. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-158 Realisierung des ADT BINTREE in Java … public interface BinTree { int getMark (); void setMark(int value); BinTree subleft(); BinTree subright(); boolean iseps(); } public class BinTreeImpl implements BinTree { private int val; private BinTree left; private BinTree right; private boolean iseps; © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-159 … Realisierung des ADT BINTREE in Java … public BinTreeImpl(int val, BinTree left, BinTree right) { this.val = val; this.left = left; this.right = right; iseps = false; } public BinTreeImpl() { this(-1, null, null); iseps = true; } public boolean iseps() { return iseps; } © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-160 … Realisierung des ADT BINTREE in Java … public int getMark () { if (iseps()) { System.out.println(("getMark: error"); return -1; } else return val; } public void setMark (int value) { if (iseps()) System.out.println("setMark: error"); else val = value; } © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-161 … Realisierung des ADT BINTREE in Java … public BinTree subleft () { if (iseps()) { System.out.println("subleft: error"); return this; } else return left; } public BinTree subright () { if (iseps()) { System.out.println("subright: error"); return this; } else return right; } } © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-162 … Realisierung des ADT BINTREE in Java • Erzeugung eines Baumes 1 2 3 durch: BinTree b2 = new BinTreeImpl(2, new BinTreeImpl(), new BinTreeImpl()); BinTree b3 = new BinTreeImpl(3, new BinTreeImpl(), new BinTreeImpl()); BinTree b1 = new BinTreeImpl(1, b2, b3); © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-163 Zusammenfassung • Wertebereiche, Typen, Attribute und Klassen • Beschreibung der Funktionalität von Datentypen • Realisierung von ADTs in Java • Dynamische Datenstrukturen • Literatur: A. Asperti, G. Longo: Categories, Types and Structures, Foundations of Computing Series, MIT Press, Cambridge, MA, 1991. G. Birkhoff: Structure of Abstract Algebras, Proc. Cambridge Philosophical Society, Vol. 31, p. 433 - 454, 1938. H. Ehrig, B. Mahr: Fundamentals of Algebraic Specification, Springer, Berlin, 1985. J. A. Goguen, J. W. Thatcher, E. G. Wagner: An Initial Algebra Approach to the Specification, Correctness and Implementation of Abstract Data Types, in R. T. Yeh, Editor, Current Trends in Programming Methodology Vol IV, Prentice Hall, Englewood Cliffs, N.J., 1978. Bertrand Meyer: Objektorientierte Softwareentwicklung, Hanser, 1990. © Klaus Hinrichs Informatik I – Datentypen und Datenstrukturen 3-164