Vorlesung „Programmieren“ Java Generics und Java API Prof. Dr. Stefan Fischer Institut für Telematik, Universität zu Lübeck https://www.itm.uni-luebeck.de/people/fischer Programme verarbeiten Daten • Häufig benötigt: Datenstrukturen zum Speichern • Bisher: Spezielle Datenstrukturen pro Programm: Security - 04 Cryptology #2 Generische Datenstrukturen • Häufig benötigte Datenstrukturen (ausführlich in AuD im 2. Semester) – Listen – Keller bzw. Stapel (Stack) – Warteschlangen (Queues) – Mengen – ... • Natürlich kann man diese selbst programmieren Security - 04 Cryptology #3 Beispiel: Eigene generische Liste Security - 04 Cryptology #4 Mögliche Implementierung Speichert alle in Java möglichen Objekte, da alle transitiv von Objekt erben Es können (bei dieser Implementierung) maximal maxElemente gespeichert werden in einer konkreten Instanz Security - 04 Cryptology #5 Mögliche Implementierung Security - 04 Cryptology #6 Mögliche Implementierung Security - 04 Cryptology #7 Mögliche Implementierung Security - 04 Cryptology #8 Mögliche Implementierung • Was praktisches: schöne „Ausgabe“ der Instanz Security - 04 Cryptology #9 Verwendung Security - 04 Cryptology #10 Der Obstkorb und die Liste Security - 04 Cryptology #11 Problem dieser Implementierung • Verwendung einer festen maximalen Größe – Werden mehr Elemente eingefügt als Platz verfügbar ist, werden diese nicht gespeichert • Besser: dynamische Größenanpassung – Liste sollte dynamisch „mitwachsen“ • Alternative Implementierung – siehe nächste Folie Security - 04 Cryptology #12 Dynamische Liste Security - 04 Cryptology #13 Datenstrukturen und Java • Dieses Problem betrifft praktisch alle Programme – In vielen Sprachen (z.B. C) hat praktisch jedes Programm seine eigenen Datentypen implementiert – Probleme: viel unnötiger Code, Fehlerquelle, schwer auszutauschen, jedes Programm war „einzigartig“ hohe Einarbeitungszeit • Daher: Java stellt Standardtypen bereit – Teil der Java Distribution – Sog. „Java Collections Framework“ (java.util.*) • Referenzen – http://download.oracle.com/javase/tutorial/collections/ – http://download.oracle.com/javase/7/docs/api/ 14 Java Collections Framework (JCF) • Stellt sog. Container-Klassen bereit – Container nehmen andere Datentypen in sich auf – Dynamische Datenstrukturen, die ihre Größe anpassen • Zwei grundlegende Typen: Collection und Map – Collection (java.util.Collection) in dieser Vorlesung • Listen, Mengen, Schlangen (Queues) – Map für Assoziativspeicher (java.util.Map) • Speichert <name, wert>-Tupel 15 Java Collections Framework (JCF) • Interfaces legen den Funktionsumfang eines Containertyps fest – z.B. das Interface Collection • Konkrete Klassen erben von diesen und implementieren die Funktion des Containers – Abstrakte Basisklassen dienen zur vereinfachten Implementierung konkreter Klassen • Beispiel Implementiert Interface „Collection“ Abstrakte Listenimplementierung Konkret, Speicherung in Array 16 Teil der Vererbungshierarchie Collection AbstractList ArrayList ... AbstractSet Vector TreeSet Security - 04 Cryptology ... HashSet #17 Collection Interface • Wichtige Methoden – – – – – – boolean add(E e); void clear(); boolean contains(Object o); int size(); Iterator iterator(); ... • Was ist wohl ein Iterator? – Später... Security - 04 Cryptology #18 Interface java.util.Collection 19 Instanziierung einer Collection • Beispiel: Verwendung einer Liste – Zunächst Wahl einer Implementierung – Dann Instanziierung •Collection c = new ArrayList(); 20 Verwendung von Collection import java.util.*; public class NutzungVonCollection { private static void test( Collection c ) { for ( int i = 0; i < 5; i++ ) c.add( i ); System.out.println(“Ist leer? “ + c.isEmpty()); System.out.println( “Anzahl Elemente: “ + c.size()); } } 21 Konzept der Iteratoren • Dient dem Zugriff auf Elemente einer Collection – Im einfachsten Fall kann man nur vorwärts von einem Element zum nächsten gelangen – Iterator iterator = collection.iterator(); • Zwei wesentliche Methoden eines Iterators – boolean hasNext(): true, wenn es ein weiteres Element gibt – Object next(): Liefert nächstes Objekt • Beispiel: Iteration über Collection next() hasNext: true next() hasNext: true 1324 next() hasNext: true 234 next() hasNext: true 333 next() hasNext: true 445 next() hasNext: true 54343 next() hasNext: true 6434 next() hasNext: true 75 hasNext: false 8345 Security - 04 Cryptology #22 Verwendung von Iteratoren • Vorsicht: nicht 2x next() aufrufen pro 1x hasNext() – z.B. innerhalb der while-Schleife 23 Verwendung von Iteratoren • Oft besser: Referenz auf nächstes Objekt einer Variable zuweisen 24 Kürzere Variante: for-Schleife • Iterationen über Collections lassen sich mit der for-Schleife prägnant und kurz schreiben – Initialisierung: Zuweisung einer Iterator-Referenz – Bedingung: Prüfen auf hasNext(); – Inkrement: Nichts tun Security - 04 Cryptology #25 Noch kürzere Variante: foreach-Schleife • Seit Java 1.5 gibt es noch eine weitere Schleife – Speziell für die Iteration über Collections, Arrays, etc. – Abkürzende Schreibweise, kann durch for-Schleife ersetzt werden – Möglich für alle Objekte, die das Interface „Iterable“ implementieren – Also z.B. Collection • Syntax: – for(Typ variable : iterableObject ) statement; Security - 04 Cryptology #26 Das Interface „Iterable“ 27 Foreach und Collections • Beispiel: Umwandlung einer for-Schleife in eine foreach-Schleife Security - 04 Cryptology #28 Collections und ihre Daten • Beispiel – c.add( new String(“1“) ); c.add( new Integer(42) ); • Collection enthält String- und Integer-Instanzen – add-Methode akzeptiert nur Object-Instanzen – Iterator kann daher nur Object zurückgeben • Beispielhafte Iteration über diese Collection – for(Object o : c) System.out.println(“Länge: “ + ((String)o).length()); • Problem: Typsicherheit – Wie stellt man sicher, dass in einer Collection nur Daten eines bestimmten Typs gespeichert werden können? 29 Wie speichert man Daten? • class IntBox { private int val; } • class StringBox { private String val; void setValue( int val ) { this.val = val; } void setValue( String val ) { this.val = val; } int getValue() { return val; } String getValue() { return val; } } 30 Wie implementiert man „generische“ Typen? • • Häufig durch Speichern von ObjectInstanzen • Verwendung – Box b = new Box(); Point p = new Point(1,2); class ObjectBox { private Object val; b.setValue( p ); ((Point)b.getValue()).getX(); void setValue( Object val ) { this.val = val; } • Probleme – Umständlich • Object getValue() { return val; } Jeder Zugriff erfordert Typ-Cast – Unsicher • • Typ-Cast kann fehlschlagen ((Vector)b.getValue()).doX(); ClassCastException } 31 Lösung: Generische Typen in Java • Sog. Java Generics – Klassen und Methoden können „generisch“ implementiert werden • Statt Object wird ein Platzhalter-Typ verwendet – Alt: private Object val; – Neu: private T val; • Dieser Typ kann wie ein normaler Typ verwendet werden – Bei der konkreten Verwendung muss T spezifiert werden Security - 04 Cryptology Lösung: Java Generics • class ObjectBox { private Object val; } • class Box<T> { private T val; #32 Sagt Java, dass T im Folgenden ein Platzhalter ist void setValue( Object val ) { this.val = val; } void setValue(T val) { this.val = val; } Object getValue() { return val; } T getValue() { return val; } } 33 Verwendung • Instanziierung – Box<String> stringBox = new Box<String>(); – Box<Integer> intBox = new Box<Integer>(); – Box<Point> pointBox = new Box<Point>(); • Zugriff – Point p = new Point(1,2); pointBox.setValue( p ); double x = pointBox.getValue().getX(); getValue() liefert Point zurück 34 Generics: Einfache generische Methoden • class HopOderTop { public static <T> T aOderB(T a, T b) { if (Math.random() > 0.5) return a; else return b; } } • System.out.println( HopOderTop.aOderB(“Zuhören“, “Weiterschlafen“) ); • System.out.println( “Note: “ + HopOderTop.aOderB( 1.0, 1.3 ) ); 35 Seit Java 1.5 verwendet das JCF Generics • Alt: Collection l = new ArrayList(); • Neu: Collection<String> = new ArrayList<String>(); • Vorteile – Es können keine falschen Datentypen mehr gespeichert werden – Typ-Cast entfällt – Beispiele: nächste Folie 36 Beispiel: Ohne und mit Generics Security - 04 Cryptology #37 Listen • Bisher: Collections – Sammlung von Objekten – Nicht notwendigerweise in einer bestimmten Reihenfolge • Listen – Feste Reihenfolge von Elementen – Zugriff auf das n-te Element • Einfügen, Löschen • Wichtige (neue) Methoden – get(index), remove(index), indexOf(Object), listIterator() • Volle Referenz – http://docs.oracle.com/javase/7/docs/api/java/util/List.html Security - 04 Cryptology #38 Implementierungen von java.util.List • Vector – Nutzt intern ein Array zur Speicherung. – Gibt es seit Java 1.0 • ArrayList – Wie Vector, nur unsynchronisiert Nur wichtig bei nebenläufigem Zugriff • LinkedList – Verkettete Elemente 39 ListIterator-Interface (extends Iterator) • • • • • • • • • void add(E e) boolean hasNext() boolean hasPrevious() E next() int nextIndex() E previous() int previousIndex() void remove() void set(E e) Security - 04 Cryptology #40 ListIterator: Beispiel • List<String> list1 = new ArrayList<String>(); • list1.add( “Test1" ); list1.add( “Test2" ); list1.add( “Test3" ); • ListIterator<String> it = list1.listIterator(); • System.out.println( it.next() ); System.out.println( it.previous() ); 41 Verwendung von List • List<String> list = new ArrayList<String>(); • list.add(“Test"); list.add(0, “Test an den Anfang"); list.add(“Und hintendran"); • System.out.println(list); • list.remove(1); • for( String s : list ) System.out.println(“- “ + s); 42 Interne Realisierung von ArrayList capacity = 7 size = 5 • Hat ein Array mit bestimmter Kapazität – Speichert Größe des Arrays und – Anzahl der gespeicherten Elemente 6 5 • Zugriff – Schnell bei Zugriff auf bestimmte Elemente über einen Index • Einfügen – Kapazität verfügbar: schnelles Einfügen am Ende Peter 4 Test 3 ein 2 ist 1 das 0 Hallo – Kapazität erschöpft: Es wird ein neues, größeres Array angelegt und alle Elemente kopiert Langsam – Einfügen in der Mitte: Es werden alle darüber liegenden Elemente um eins nach oben kopiert Langsam Interne Realisierung von LinkedList • Klasse LinkedList selbst hält nur zwei Referenzen – Auf erstes und letztes Element • Jedes Datenelement hat Referenz auf Vorgänger und Nachfolger • Einfügen an bestimmtem Index – Suchen des n-ten Elements und einfügen durch „Umbiegen“ der Referenzen – Besonders effizient am Anfang und am Ende Klasse LinkedList (implementiert List) 88 Referenz auf erstes Element 1 77 88 Referenz auf letztes Element 9 3 1 Suchen in Listen • Methode indexOf sucht (kleinsten) Index eines Objektes • Beispiel – List<Integer> list = Arrays.asList( 1, 3, 4, 1 ); – int i = list.indexOf( 1 ); – System.out.println( i ); 45 Wie vergleicht man Objekte? • Wie funktioniert der Vergleich von Objekten? – == funktioniert nicht (vergleicht Objektreferenz) • Zwei Interfaces in Java: Comparable, Comparator • Eine Klasse implementiert Comparable – Jede Instanz kann sich mit einer anderen Instanz vergleichen • Eine andere Klasse implementiert Comparator – Erhält zwei Objekte und vergleicht diese miteinander 46 Comparable • Interface java.lang.Comparable • compareTo(T other) liefert – < 0, wenn „this“ kleiner „other“ – > 0, wenn „this“ größer „other“ – == 0, wenn „this“ gleich „other“ 47 Beispiel (ohne Sonderfälle) public class Mensch implements Comparable<Mensch> { private int groesse; public int compareTo(Mensch other) { return groesse – other.groesse; } } 48 Comparator • Interface java.util.Comparator • Wie compareTo von java.lang.Comparable – Vergleicht aber zwei Objekte miteinander – Vorteil: Außerhalb der Klasse implementierbar – Beispiel: Verschiedene Sortierstrategien 49 Beispiel: Sortieren von Collections • Hilfsklasse java.util.Collections – Methoden zum Sortieren von Listen – Mit und ohne extra Comparator • Ohne Angabe eines Comparators – Das Comparable Interface wird verwendet 50 Beispiel: Kommandozeilenargumenten sortieren • import java.util.*; public class Sort { public static void main(String[] args) { List<String> list = Arrays.asList(args); Collections.sort(list); System.out.println(list); } } • Ausführen: java Das ist ein Test • Ausgabe: [Das, Test, ein, ist] 51 Mengen in Java • Mengen enthalten jedes Element genau einmal – Verwenden Comparator/Comparable zur Identitätsfeststellung • In Java: Interface java.util.Set<T> – Implementierungen: HashSet, TreeSet (Auswahl) 52 Beispiel für Set • Set<String> set = new HashSet<String>(); • set.add(“Hallo“); set.add(“Hallo“); set.add(“Hallo1“); • System.out.println( “Elemente: “ + set.size() ); • for(String s : set) System.out.println(“Element: “ + s); 53 Stacks • Implementierung in Java: java.util.Stack<T> – Erbt von Vector – Ergänzt Funktionen eines Stacks 54 • Was fällt Ihnen auf bei dieser Implementierung eines Stacks? 55 • Weitere wichtige Packages von Java – java.lang.* – java.util.* – java.io.* – java.net.* 56 Security - 04 Cryptology #57