Programmieren in Java Datenstrukturen

Name: Patrick Förster, Michael Hasseler

Programmieren in Java

Was bisher geschah • Bisher wurden zur Gruppierung von Daten immer Arrays genutzt • Bis auf die Index-Funktion gibt es allerdings keinerlei logischer Operationen, mit denen der Array manipuliert werden könnte • Wenn die zu organisierende Menge dynamisch ist, machen Arrays einem das Leben daher schwer: • Arrays sind statisch: Länge einmal festgelegt, nicht mehr änderbar • „Mal eben“ ein Element hinzufügen oder entfernen ist aufwändig • Zumeist muss der Array-Inhalt kopiert werden • Große Arrays  großer Kopieraufwand • Datentypen, die eine Menge an Daten gruppieren und auf dieser Gruppierung eine gewisse Logik bereitstellen werden Datenstrukturen genannt • Datenstrukturen & Algorithmen meist eine eigene Vorlesung!

Name: Patrick Förster, Michael Hasseler

2

Programmieren in Java

Lauf- und Zugriffszeit • Wichtige Punkte bei der Auswahl einer Datenstruktur sind • Laufzeit: Die Anzahl der Schritte, um ein bestimmtes Element zu finden • Zugriffszeit: Schritte, um auf ein bestimmtes Element zuzugreifen • Beispiel Array: • Zugriffszeit: • Array liegt „aneinander gereiht“ im Speicher • Zugriff: Speicheradresse des 1. Elementes + Position des gesuchten  Zugriff in einem Schritt (Notation: Ɵ(1), „konstante Zugriffszeit“) • Laufzeit: • Im schlechtesten Fall ist das Element nicht vorhanden • Daher muss es mit allen Elementen verglichen werden  Ɵ(n), n ist die Anzahl der Elemente („lineare Laufzeit“)

Name: Patrick Förster, Michael Hasseler

3

Programmieren in Java

Collection-API • Java bietet mit der Collection-API einen Basissatz an Datenstrukturen • All diese Strukturen sind dynamisch und damit flexibler als Arrays • Jede der Strukturen basiert auf einem der folgenden Interfaces • Collection: Interface für Listen bzw. Mengen • Map: Interface für eine Gruppe assoziativ angeordneter Elemente • Sowohl Collection als auch Map sind generisch, d.h. der Datentyp der Inhalte kann generisch spezifiziert werden: Collection list = new LinkedList(); dice("Dies ist ein String der durcheinander gewürfelt wird", list);  [ein, ist, gewürfelt, String, Dies, wird, der, durcheinander]

• LinkedList ist eine Implementierung von Collection • Angenommen: dice fühlt die List in zufälliger Reihenfolge mit StringFragmenten getrennt nach dem Leerzeichen aus dem übergebenen String

Name: Patrick Förster, Michael Hasseler

4

Programmieren in Java

Das Interface Collection public interface Collection extends Iterable { boolean add(E e); boolean remove(Object o); boolean contains(Object o); int size(); boolean isEmpty(); Iterator iterator(); Object[] toArray(); T[] toArray(T[] a); boolean containsAll(Collection c); boolean addAll(Collection c); boolean retainAll(Collection c); void clear(); }

Name: Patrick Förster, Michael Hasseler

5

Programmieren in Java

Das Interface Collection • Jede Implementierung des Collection-Interfaces unterstützt das • Hinzufügen und Entfernen von Elementen (einzeln oder massenhaft) • Überprüfen ob gewisse Inhalte vorhanden sind • Abfragen der Größe • Umwandeln in einen Array • Desweiteren erweitert Collection das Interface Iterable • Dies schreibt die Methode iterator() vor • Alle Klassen, die dieses Interface implementieren können in der foreach-Schleife genutzt werden: Collection list = new LinkedList(); dice("Dies ist ein String der durcheinander wird", list); for (String element : list) { System.out.println(element); }

Name: Patrick Förster, Michael Hasseler

6

Programmieren in Java

Listen • Listen sind Collections mit einer Ordnungshierarchie • Auf einen bestimmten Inhalte innerhalb der Liste kann über seinen Index (~ Position) zugegriffen werden List list = new LinkedList(); String fragment = list.get(3);

// füllen mit dice

• List ist ein Interface mit verschiedenen bereits vorhandenen Implementierungen: • ArrayList: das dynamische Pendant zum Array • Vector: siehe ArrayList • LinkedList: Die Elemente sind miteinander verkettet (nächste Folie) • Sowohl ArrayList als auch Vector greifen intern auf Arrays zurück • Die Unterschiede liegen in der Thread-Sicherheit (Vector) und der Art und Weise wie der interne Array verwaltet wird

Name: Patrick Förster, Michael Hasseler

7

Programmieren in Java

LinkedList • Jedes Element in der LinkedList wird in einem „Node“ gespeichert • Jeder Node kennt seinen Vorgänger und seinen Nachfolger • Der erste und letzte Node sind in der Liste „verlinkt“

Node first

last

data

Name: Patrick Förster, Michael Hasseler

8

Programmieren in Java

LinkedList (Code) • Node-Klasse LinkedListNode public class LinkedListNode { protected LinkedListNode prev; protected LinkedListNode next; protected DATA data;

protected LinkedListNode(LinkedListNode prev, LinkedListNode next, DATA data) { this.prev = prev; this.next = next; this.data = data; } public String toString() {return data == null ? "" : data.toString();}

}

• Man beachte: • Der Konstruktor ist protected  Instanzen nur innerhalb des eigenen Paketes erzeugbar • Keine Getter/Setter, da auch die Attribute protected

Name: Patrick Förster, Michael Hasseler

9

Programmieren in Java

LinkedList (Code II) public class LinkedList /* … */ { private LinkedListNode first = null; private LinkedListNode last = null; public LinkedList() {} protected LinkedListNode getNode(int index) { LinkedListNode node = first; int i = 0; while (i != index && node != null) { node = node.next; i++; } return node; }

public DATA get(int index) { LinkedListNode node = getNode(index); return node == null ? null : node.data; } … }

Name: Patrick Förster, Michael Hasseler

10

Programmieren in Java

Lauf- und Zugriffzeit (ArrayList, LinkedList) • ArrayList/Vector: • Intern werden die Elemente in einem Array gespeichert • Zugriffszeit: Ɵ(1) • Laufzeit: Ɵ(n) • LinkedList • Elemente sind verkettet • Kein direkter Zugriff • Um das n-te Element zu erreichen, muss die Kette vom ersten bis zum n-1 Element durchlaufen werden  Zugriffszeit: Ɵ(n) • Im schlechtesten Fall ist ein gesuchtes Element nicht vorhanden • Es muss die gesamte Kette durchlaufen werden, um dies festzustellen  Laufzeit: Ɵ(n) Name: Patrick Förster, Michael Hasseler

11

Programmieren in Java

LinkedList • Suche eines Elements in einer LinkedList ist vergleichsweise langsam • Das Löschen und Hinzufügen jedoch ist schneller als bei ArrayList/Vector • Da jeder Node seinen Vorgänger und Nachfolger kennt, lassen sich beide Operationen durch „umbiegen“ der Verlinkung in einem Schritt durchführen • ArrayList/Vector müssen die richtige Stelle zum Löschen finden, den Array unter Umständen kopieren und neufüllen Löschen first

last

Hinzufügen

first

Name: Patrick Förster, Michael Hasseler

last

12

Programmieren in Java

LinkedList (Code III) public boolean add(int index, DATA data) { if (index < 0 || index > size()) throw new IndexOutOfBoundsException(); if (first == null) { first = new LinkedListNode(null, null, data); last = first; return true ; } if (index == size()) { last.next = new LinkedListNode(last, null, data); last = last.next; return true; } LinkedListNode temp = getNode(index); LinkedListNode node = new LinkedListNode(temp.prev, temp, data); if (temp == first) first = node; if (temp.prev != null) temp.prev.next = node; temp.prev = node; return true; }

Name: Patrick Förster, Michael Hasseler

13

Programmieren in Java

LinkedList (Code IV) public boolean remove(int index) { if (index < 0 || index >= size()) throw new IndexOutOfBoundsException(); LinkedListNode node = getNode(index); if (node.prev != null) node.prev.next = node.next; if (node.next != null) node.next.prev = node.prev; if (node == first) first = node.next; if (node == last) last = node.prev; return true; } public int size() { LinkedListNode node = first; int size = 0; while (node != null) { size++; node = node.next; } return size; }

Name: Patrick Förster, Michael Hasseler

14

Programmieren in Java

Stack • Ein Stack ist bereits im Kapitel „Speichermanagement“ aufgetaucht • Der Stack-Speicher baute sich Frame für Frame auf einer Seite des Speichers auf • Nach jedem Methodenende wird der Stack auf genau der Seite wieder abgebaut • Die Stack-Datenstruktur ist analog aufgebaut • Auch LIFO (Last-In-Firs t-Out) genannt peek()

pop()

push(Object)

boolean empty(); E peek (); E pop(); E push(E o); int search(Object o);

• Zugriff: Ɵ(1) (es ist immer nur das letzte Element zugreifbar!) • Laufzeit: Ɵ(n) (es müssen alle Elemente verglichen werden) Name: Patrick Förster, Michael Hasseler

15

Programmieren in Java

Queue • Ähnlich dem Stack • Eine Queue (Warteschlange) wird „hinten“ auf- und „vorne“ abgebaut • Daher: FIFO (First-In-First-Out) E element(); boolean offer(E o); E peek(); E poll(); E remove(); element()/peek()

offer(object)

poll()/remove()

• Zugriff: Ɵ(1), Laufzeit: Ɵ(n) (beides, analog zum Stack) Name: Patrick Förster, Michael Hasseler

16

Programmieren in Java

Set • Spezielle Collection mit der Einschränkung: • Jedes Element in der Menge ist eindeutig, d.h. es gibt keine zwei Element e1 und e2 für die gilt e1.equals(e2) == true

• Sets müssen nicht zwangsläufig geordnet sein • Zugriffs- und Laufzeit variieren wie schon bei der Liste je nach Implementierung

Name: Patrick Förster, Michael Hasseler

17

Programmieren in Java

Map • Der Datentyp Map wird auch als „assoziativer Speicher“ bezeichnet • Jedes Element in der Map ist eindeutig über einen Schlüssel assoziiert • Eine Map enthält demnach Schlüssel-Wert Paare Key Key Key Key … Key

1 2 3 4 n

Value Value Value Value … Value

1 2 3 4 n

Name: Patrick Förster, Michael Hasseler

public interface Map { int size(); boolean isEmpty(); boolean containsKey(Object key); boolean containsValue(Object value); V get(Object key); V put(K key, V value); V remove(Object key); void putAll(Map