3.3 Realisierung von ADTs in Java 3.3.1 Realisierung des ADT

Werbung
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
Herunterladen