homedukeAlgorithmen & Datenstrukturen mit Java: Persistente und destruktive Datenstrukturen Prof. Dr. Uwe Schmidt FH Wedel

Persistente und destruktive Datenstrukturen

weiter

weiter

Vor- und Nachteile

Definition
destruktiv
Zur Konstruktion neuer Listen werden in vorhandenen Listen einzelne Knoten verändert.
 
Es wird mit Zuweisungen an Komponenten einzelner Knoten gearbeitet
 
...
next = next.f(...);
return this;
...
gut
Speicherplatz-Effizienz
schlecht
Seiteneffekte
persistent
Zur Konstruktion neuer Listen werden NIE in vorhandenen Listen einzelne Knoten verändert, sondern IMMER nur gelesen und daraus neue Knoten mit den passenden Elementen erzeugt.
 
Es wird NIE mit Zuweisungen an Komponenten einzelner Knoten gearbeitet
 
...
return next.f(...).cons(info);
...
schlecht
Speicherplatz-Effizienz
gut
keine Seiteneffekte
weiter
Vereinheitlichung
Die Codestücke, in denen mit Zuweisungen (destruktiv) oder mit Erzeugung neuer Objekte (persistent) gearbeitet wird, können isoliert werden.
gut
Beide Varianten können aus einer Codebasis abgeleitet werden
Lösung
Setter-Methoden
Beispiel
destruktiv
LinkedList setNext(LinkedList l) {
  next = l;
  return
    this;
}
persistent
LinkedList setNext(LinkedList l) {
  return
    new Node(this.infol);
}
kürzer
LinkedList setNext(LinkedList l) {
  return
    l.cons(info);
}
Argumente
destruktiv
Listen als Argumente werden verbraucht.
 
Diese Listen dürfen nicht weiter verwendet werden.
Beispiel
falsch
...
l2 = l1.append(x);
...
l3 = l1.append(y);
...
 
Listen müssen geklont werden
richtig
...
l2 = l1.copy().append(x);
...
l3 = l1.append(y);
...
schlecht
Sehr fehleranfällig
schlecht
Kann auch zu Speicher- und Laufzeit-Ineffizienzen führen
persistent
Listen als Argumente werden nur gelesen und behalten somit ihre Werte.
 
Sie dürfen beliebig weiterverwendet werden.
richtig
...
l2 = l1.append(x);
...
l3 = l1.append(y);
...
gut
keinerlei Fehlerquelle
weiter
Mehrfachverwendung
destruktiv
Listen dürfen auch in einer Operation nie mehrfach verwendet werden.
Beispiel
falsch
...
l2 = l1.concat(l1);
...
?
Welches Ergebnis liefert l2.length()?
richtig
...
l2 = l1.copy().concat(l1);
...
schlecht
sehr fehleranfällig
persistent
Listen dürfen beliebig häufig verwendet werden.
richtig
...
l2 = l1.concat(l1.concat(l1));
...
gut
keinerlei Fehlerquelle
weiter
Sharing
Gemeinsame Nutzung von Teilstrukturen
destruktiv
nicht möglich
falsch
...
l2 = l1.cons(x);
l1 = l1.append(y);
...
richtig
...
l2 = l1.copy().cons(x);
l1 = l1.append(y);
...
schlecht
Speicherplatz-Effizienz
persistent
Listen dürfen gemeinsame Teile besitzen
 
...
l2 = l1.cons(x);
l1 = l1.append(y);
...
gut
keinerlei Fehlerquelle, das Verdoppeln geschieht automatisch
weiter
Speicherverwaltung
destruktiv
händisch: Durch Freigabe aller Knoten
gut
(relativ) einfach
persistent
händisch (in C): Zum Beispiel durch Referenz-Zähler
schlecht
schwierig, aufwändig
gut
Bei Sprachen mit automatischer Speicherverwaltung (Java, Javascript, Python, Ruby, Scala, Haskell, ...) entfällt dieses Problem
weiter
Korrektheit
destruktiv
schlecht
Sehr fehleranfällig durch viele verschiedene Zugriffspfade auf die gleichen Knoten und durch Zuweisungen an Komponenten der Knoten
persistent
gut
Einfach: Es gibt keinen Unterschied zwischen einfachen Werten und Listen.
weiter
merke
Der hier für Listen gemachte Vergleich ist allgemeingültig für alle dynamischen Datenstrukturen, z.B. für Suchbäume, Warteschlangen, Hash-Tabellen, ...
?

Strings sind Listen von Zeichen.

Welche Implementierungsstrategie für Strings ist in Java gewählt worden?


Letzte Änderung: 01.11.2017
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel