Programmierung 1 Studiengang MI / WI
Dipl.-Inf., Dipl.-Ing. (FH) Michael Wilhelm
Hochschule Harz
FB Automatisierung und Informatik
mwilhelm@hs-harz.de
http://mwilhelm.hs-harz.de
Raum 2.202
Tel. 03943 / 659 338
Inhalt der Vorlesung
Überblick:
•
Erste Beispiele, Interaktion
•
elementare Datentypen
•
Variablen und Kontrollstrukturen
•
Arrays und Funktionen
•
Objekte und Methoden
•
Algorithmen und Pseudocode
•
Laufzeitverhalten
•
Simulation
•
Bibliotheken
•
Template und generische Klassen
• Folien basierend auf Daniel Schiffman “Learning Processing” und Donald W. Smith
• Folien basierend auf Vorlesung „Programmierung1“ von Prof. Singer
Grundlegende Algorithmen und Methoden:
•
Suchen und Sortieren
•
Hashing
•
Rekursion
•
Graphen
•
Dynamische
Programmierung
Von Processing zu Java
Kapitel
Templates
•
Generisches Interface
•
Generische Klasse
Schnelles Suchen (Hashing)
•
Hash-Funktion
•
Hash-Map
•
Hash-Set
Generische Klassen
•
Klassen können mit einem oder mehreren zusätzlichen Parametern versehen werden. Diese stehen während der Implementierung als Platzhalter für Klassennamen.
•
Bei der Instanziierung sind dann für diese Platzhalter konkrete Klassen einzusetzen.
Generische Klassen dienen der Typsicherheit. Der Compiler ersetzt beim übersetzen alle
parametrisierten Klassen mit der Klasse Object!
Generische Klassen
public class GenKlasse<T> { private T[] array;
public GenKlasse(int n) {
array = (T[]) new Object[n]; // nicht new T[n] !!!
} ...
}
Benutzung:
Generisches Interface
interface Compareable<T> { public int compareTo(T o);
}
class Klasse implements Comparable<Klasse> { ...
public int compareTo(Klasse o) { int result = 0;
...
return result;
} }
in java.lang vorhanden, daher kein Import notwendig
Vertrag
Vertrags-
erfüllung
•
Klasse[] k = new Klasse[10];
•
Klasse k0 = new Klasse();
•
// Werte setzen ...
•
Arrays.sort(array); // sortieren durch Schnittstelle
•
Arrays.binarySearch(array, k0);
Generisches Interface
•
Klasse key = new Klasse(5);
•
Collections.sort(array);
•
Collections.binarySearch(array, key);
Generisches Interface: Beispiel
private void test1() {
GenKlasse<Integer> tempI = new GenKlasse(5);
tempI.setItem(0, 12);
tempI.setItem(1, -12);
tempI.setItem(2, 16);
tempI.setItem(3, 2);
tempI.setItem(4, 18);
Integer I = new Integer(12);
System.out.println("Contain "+I+": "+tempI.contain(I));
System.out.println("Contain "+I+": "+tempI.contain(12));
System.out.println("Contain "+I+": "+tempI.contain(17));
}
public class GenKlasse<T> { private T[] array;
public GenKlasse(int n) {
array = (T[]) (new Object[n]);
}
public void setItem(int i, T item) { array[i] = item;
} // add
public void print() {
for(Object obj: array) { T item = (T) obj;
System.out.println(item);
}
public boolean contain(T itemSearch) { for(Object obj: array) {
T item = (T) obj;
if (item.equals(itemSearch)) return true;
}
return false;
Hashing / HashTable / HashMap / HashSet
Definition Hashing:
•
Die Objekte werden in einem Feld mit Indizies 0 bis N-1 gespeichert.
•
Die einzelnen Speicherpositionen werden Buckets genannt.
•
Eine Hashfunktion h : e --> N bestimmt für ein
Schlüsselelement e einen Bucket h(e) im Feld, in dem ein mit dem Schlüssel e assoziertes Element gespeichert werden soll.
•
Die Wahl der Funktion h hat entscheidenden Einfluss auf die Qualität des Verfahrens.
Kollision:
Beispiel:
Wir wählen die ganzen Zahlen als Element e. Ein Feld der Größe N. Die Hashfunktion sei h(e) = e mod N. Gespeichert werden
sollen 42 und 119. Hier ist N = 10.
Problem:
•
Es soll noch der Wert 69 gespeichert werden. Jedoch ist 69 mod 10 = 9. Dieser Bucket ist bereits belegt. Wie soll bei einer Kollision verfahren werden?
Beachte:
•
Man kann zeigen: Idealerweise ist die Größe der Hashtabelle eine Primzahl, die aber nicht nahe bei einer Zweierpotenz liegen sollte.
Index 0 1 2 3 4 5 6 7 8 9
Eintrag 42 119
Hashfunktionen
•
Hashfunktionen sollen “gute” Eigenschaften besitzen.
•
Insbesondere sollen sie die zur Verfügung stehenden Buckets möglichst gleichmäßig füllen.
•
Sie sollen aber auch einfach berechenbar sein, denn die Kosten ein Element zu speichern sind direkt proportional zu den
Kosten der Berechnung der Hashfunktion.
•
Der Zugriff auf Elemente einer Hashtabelle ist O(1).
Hashfunktionen
Umgang mit Kollisionen:
•
Verkettung:
•
Alle kollidierenden Elemente werden im selben Bucket gespeichert, etwa in einer ArrayList.
•
Sondieren: suche einen freien Bucket
•
lineares Sondieren
•
quadratisches Sondieren
Doppelte Hashwerte in einer Liste speichern
•
Ein Überläufer ist ein Element, welches eine bereits gefüllte Position in der Hashtabelle zum Überlaufen bringt. Bei der Verkettung der Überläufer werden diese für jeden Bucket in einem Array [oder einer verketteten Liste (siehe 2. Semester)]
untergebracht.
•
Bei vielen Überläufern kann die Tabelle entarten, d.h. viele Elemente befinden sich in einem gemeinsamen Bucket, mit Zugriffszeiten von O(N).
Index 0 1 2 3 4 5 6 7 8 9
Eintrag
Lineares Sondieren
•
Bei einer Kollision für T[h(e]] werden der Reihe nach die Buckets überprüft
•
T[h(e) + 1]; T[h(e) + 2], ...
•
Das Element wird im ersten so gefundenen freien Bucket abgespeichert.
•
Hierbei bezeichnet T[x] den zu x mod N gehörigen Bucket.
Index 0 1 2 3 4 5 6 7 8 9
Eintrag 69 29 42 119
Quadratisches Sondieren
•
lineares Sondieren führt oft zu einer Konzentration der Elemente in der Nähe weniger Buckets.
•
Quadratisches Sondieren verhindert dies.
•
T[h(e) + 1]; T[h(e) + 4]; T[h(e) + 9]; ....
Index 0 1 2 3 4 5 6 7 8 9
Eintrag 69 42 29 119
Re-Hashing
•
Wird keine Verkettung der Überläufer durchgeführt, so muss die Hashtabelle groß genug sein um alle zu speichernden
Elemente aufnehmen zu können.
•
Sollte dies nicht der Fall sein, muss sie entsprechend vergrößert werden.
•
Dann muss für jeden Eintrag ein neuer Bucket, entsprechend
der neu berechneten Hashfunktion, bestimmt werden.
Hashfunktion und equals
•
Beachte: Falls die Hashfunktion für zwei Objekte
denselben Wert ergibt, folgt hieraus nicht, dass die beiden Objekte gleich sind.
•
Aber da zwei gleiche Objekte denselben Hashwert haben müssen, heißt dies auch:
•
Falls der Hashwert zweier Objekte nicht gleich ist, können auch die Objekte nicht gleich sein.
•
In mathematischer Notation für die Objekte A und B sowie ihre Hashwerte h
Aund h
B:
A = B ⇒ h
A= h
B⇔ h
A≠ h
B⇒ A ≠ B
hashcode und equals
•
Falls Objekte in Hashtabellen (oder
allgemeiner in Collections) gespeichert werden sollen, muss immer eine
entsprechende equals-Methode
implementiert sein. Diese muss immer konsistent mit der hashCode-Methode sein.
•
Also, falls Sie equals implementieren, dann sollten Sie auch hashCode
implementieren und umgekehrt
•
Eclipse kann equals und hashCode für eine Klasse automatisch erzeugen.
Menü Source
@Override
public int hashCode() { final int prime = 31;
int result = 1;
if (firma == null)
result=prime*result+
firma.hashCode();
else
result = 0;
result = prime * result + kw;
return result;
}
@Override
public boolean equals(Object obj) { if (this == obj)
return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Auto other = (Auto) obj;
if (firma == null) {
if (other.firma != null) return false;
} else if (!firma.equals(other.firma)) return false;
if (kw != other.kw) return false;
return true;
}
HashMap / HashTable in Java
•
HashMap in Java: Schlüssel (hier String) und Wert (hier Klasse) getrennt.
•
Hashtable ist synchronisiert, HashMap ist das nicht (schneller) HashMap<String, Klasse> hash;
hash = new HashMap<String, Klasse>();
hash.put("variable", new Klasse());
Klasse k = hash.get("variable");
•
typische Anwendung:
•
Datenbanken
•
mögliche Schlüssel:
HashSet in Java (verwaltet eine Menge)
•
HashSet in Java: Schlüssel (via hashCode) und Wert gemeinsam in Klasse.
HashSet<Klasse> hash = new HashSet<Klasse>();
Klasse k = new Klasse();
hash.add(k);
boolean found = hash.contains(k);
•
HashSet hat Eigenschaften einer Menge, d.h. kein Element kann mehrfach auftreten (equals).
•
Dies muss gegebenenfalls vor dem Einfügen überprüft
werden.
Zusammenfassung
•
Interfaces erlauben Verträge über zu implementierende Methoden in Klassen abzuschließen.
•
Generische Klassen dienen der typsicheren Programmierung.
Der Compiler entfernt beim Übersetzen diese Information.
•
ArrayList<T> implementiert dynamisch wachsende Arrays.
•
Hashing berechnet den Index in einem Array an dem Information abgelegt werden soll.
•
Hash-Indizes sind nicht eindeutig -- es können Kollisionen auftreten.
•