Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger. 
Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Objektorientierte Beziehungsfragen
7 Ausnahmen müssen sein
8 Äußere.innere Klassen
9 Besondere Typen der Java SE
10 Generics<T>
11 Lambda-Ausdrücke und funktionale Programmierung
12 Architektur, Design und angewandte Objektorientierung
13 Komponenten, JavaBeans und Module
14 Die Klassenbibliothek
15 Einführung in die nebenläufige Programmierung
16 Einführung in Datenstrukturen und Algorithmen
17 Einführung in grafische Oberflächen
18 Einführung in Dateien und Datenströme
19 Einführung ins Datenbankmanagement mit JDBC
20 Einführung in <XML>
21 Testen mit JUnit
22 Bits und Bytes und Mathematisches
23 Die Werkzeuge des JDK
A Java SE-Paketübersicht
Stichwortverzeichnis


Download:

- Beispielprogramme, ca. 35,4 MB


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 9 Besondere Typen der Java SE
Pfeil 9.1 Object ist die Mutter aller Klassen
Pfeil 9.1.1 Klassenobjekte
Pfeil 9.1.2 Objektidentifikation mit toString()
Pfeil 9.1.3 Objektgleichheit mit equals(…) und Identität
Pfeil 9.1.4 Klonen eines Objekts mit clone() *
Pfeil 9.1.5 Hashwerte über hashCode() liefern *
Pfeil 9.1.6 System.identityHashCode(…) und das Problem der nicht eindeutigen Objektverweise *
Pfeil 9.1.7 Aufräumen mit finalize() *
Pfeil 9.1.8 Synchronisation *
Pfeil 9.2 Die Utility-Klasse java.util.Objects
Pfeil 9.3 Vergleichen von Objekten
Pfeil 9.3.1 Natürlich geordnet oder nicht?
Pfeil 9.3.2 Die Schnittstelle Comparable
Pfeil 9.3.3 Die Schnittstelle Comparator
Pfeil 9.3.4 Rückgabewerte kodieren die Ordnung
Pfeil 9.3.5 Statische und Default-Methoden in Comparator
Pfeil 9.4 Wrapper-Klassen und Autoboxing
Pfeil 9.4.1 Wrapper-Objekte erzeugen
Pfeil 9.4.2 Konvertierungen in eine String-Repräsentation
Pfeil 9.4.3 Von einer String-Repräsentation parsen
Pfeil 9.4.4 Die Basisklasse Number für numerische Wrapper-Objekte
Pfeil 9.4.5 Vergleiche durchführen mit compare(…), compareTo(…), equals(…) und Hashwerten
Pfeil 9.4.6 Statische Reduzierungsmethoden in Wrapper-Klassen
Pfeil 9.4.7 Die Größe eines primitiven Typs in den Wrapper-Konstanten SIZE und BYTES
Pfeil 9.4.8 Behandeln von vorzeichenlosen Zahlen *
Pfeil 9.4.9 Die Klasse Integer
Pfeil 9.4.10 Die Klassen Double und Float für Fließkommazahlen
Pfeil 9.4.11 Die Long-Klasse
Pfeil 9.4.12 Die Boolean-Klasse
Pfeil 9.4.13 Autoboxing: Boxing und Unboxing
Pfeil 9.5 Iterator, Iterable *
Pfeil 9.5.1 Die Schnittstelle Iterator
Pfeil 9.5.2 Wer den Iterator liefert
Pfeil 9.5.3 Die Schnittstelle Iterable
Pfeil 9.5.4 Erweitertes for und Iterable
Pfeil 9.5.5 Interne Iteration
Pfeil 9.5.6 Einen eigenen Iterable implementieren *
Pfeil 9.6 Die Spezial-Oberklasse Enum
Pfeil 9.6.1 Methoden auf Enum-Objekten
Pfeil 9.6.2 Aufzählungen mit eigenen Methoden und Initialisierern *
Pfeil 9.6.3 enum mit eigenen Konstruktoren *
Pfeil 9.7 Zum Weiterlesen
 

Zum Seitenanfang

9.5Iterator, Iterable * Zur vorigen ÜberschriftZur nächsten Überschrift

In Programmen spielen nicht nur einzelne Daten eine Rolle, sondern auch Sammlungen dieser Daten. Arrays sind zum Beispiel solche Sammlungen, aber auch Standarddatenstrukturen wie ArrayList oder HashSet oder Dateien. Eine häufige Anforderung ist, an die Daten dieser Sammlungen zu kommen, sie also auszulesen, aber auch diese Sammlungen zu beschreiben, sprich Daten hinzuzufügen. Natürlich hat jede dieser veränderbaren Datenstrukturen eine bestimmte API, doch im Sinne der guten objektorientierten Modellierung ist es wünschenswert, dieses Verhalten in Schnittstellen zu beschreiben. Zwei Schnittstellen treten besonders hervor:

  • Iterator: Bietet eine Möglichkeit, Schritt für Schritt durch Sammlungen zu laufen.

  • Iterable: Liefert so einen Iterator, ist also ein Iterator-Produzent.

 

Zum Seitenanfang

9.5.1Die Schnittstelle Iterator Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Iterator ist ein Datengeber, der über eine Methode verfügen muss, die das nächste Element liefert. Dann muss es eine zweite Methode geben, die Auskunft darüber gibt, ob der Datengeber noch weitere Elemente zur Verfügung stellt. Genau dafür deklariert die Java-API eine Schnittstelle Iterator mit zwei Operationen:

Hast du mehr?

Gib mir das Nächste!

Iterator

hasNext()

next()

Tabelle 9.9Zwei zentrale Methoden des Iterators

[zB]Beispiel

Das Ablaufen (auf Neudeutsch »iterieren«) sieht immer gleich aus:

while ( iterator.hasNext() )

process( iterator.next() );

Die Methode hasNext() ermittelt, ob es überhaupt ein nächstes Element gibt, und wenn ja, ob next() das nächste Element erfragen darf. Bei jedem Aufruf von next() erhalten wir ein weiteres Element der Datenstruktur. So kann der Iterator einen Datengeber (in der Regel eine Datenstruktur) Element für Element ablaufen. Übergehen wir ein false von hasNext() und fragen trotzdem mit next() nach dem nächsten Element, bestraft uns eine NoSuchElementException.

interface java.util.Iterator<E>
  • boolean hasNext()

    Liefert true, falls die Iteration weitere Elemente bietet.

  • E next()

    Liefert das nächste Element in der Aufzählung oder NoSuchElementException, wenn keine weiteren Elemente mehr vorhanden sind.

Prinzipiell könnte die Methode, die das nächste Element liefert, auch per Definition null zurückgeben und so anzeigen, dass es keine weiteren Elemente mehr gibt, und auf hasNext() könnte verzichtet werden. Allerdings kann null dann kein gültiger Iterator-Wert sein, und das wäre ungünstig.

Die Schnittstelle Iterator erweitert selbst keine weitere Schnittstelle.[ 193 ](Konkrete Enumeratoren (und Iteratoren) können nicht automatisch serialisiert werden; die realisierenden Klassen müssen hierzu die Schnittstelle Serializable implementieren. ) Die Deklaration ist generisch, da das, was der Iterator liefert, immer von einem bekannten Typ ist.

Beim Iterator geht es immer nur vorwärts

Im Gegensatz zum Index eines Arrays können wir beim Iterator ein Objekt nicht noch einmal auslesen (next() geht automatisch zum nächsten Element), nicht vorspringen oder hin und her springen. Ein Iterator gleicht anschaulich einem Datenstrom; wollten wir ein Element zweimal besuchen, zum Beispiel eine Datenstruktur von rechts nach links noch einmal durchwandern, dann müssten wir wieder ein neues Iterator-Objekt erzeugen oder uns die Elemente zwischendurch merken. Nur bei Listen und sortierten Datenstrukturen ist die Reihenfolge der Elemente vorhersehbar.

[»]Hinweis

In Java steht der Iterator nicht auf einem Element, sondern zwischen Elementen.

Code auf verbleibenden Elementen eines Iterators ausführen

In der Schnittstelle Iterator gibt es die Default-Methode forEachRemaining(Consumer<? super E> action), die ein beliebiges Stückchen Code – transportiert über einen Consumer – auf jedem Element ausführt. Die Implementierung der Default-Methode ist ein Dreizeiler:

Listing 9.30java.util.Iterator.java, forEachRemaining()

default void forEachRemaining( Consumer<? super E> action ) {

Objects.requireNonNull( action );

while ( hasNext() )

action.accept( next() );

}

Mithilfe dieser Methode lässt sich eine externe Iteration über eine selbst gebaute Schleife in eine interne Iteration umbauen, und Lambda-Ausdrücke machen die Implementierung der Schnittstelle kurz – mehr zu Lambda-Ausdrücken in Kapitel 11, »Lambda-Ausdrücke und funktionale Programmierung«.

[zB]Beispiel

Gibt jedes Argument der Konsoleneingabe aus:

new Scanner( System.in ).forEachRemaining( System.out::println );
interface java.util.Iterator<E>
  • default void forEachRemaining(Consumer<? super E> action)

    Führt action auf jedem kommenden Element des Iterators bis zum letzen Element aus.

[»]Hinweis

Jede Collection-Datenstruktur liefert mit iterator() einen Iterator, auf dem dann wiederum ein Aufruf von forEachRemaining(…) möglich ist. Allerdings gibt es mit der Stream-API eine flexiblere Alternative zum Abarbeiten von Programmcode, die sich auch anbietet, wenn der Stream aus anderen Quellen kommt, wie Arrays.

Optional: Elemente über Iterator löschen

Die Iterator-Methode next() ist eine reine Lesemethode und verändert die darunterliegende Datenstruktur nicht. Doch bietet die Schnittstelle Iterator auch eine Methode remove(), die das zuletzt von next() gelieferte Objekt aus der Datensammlung entfernen kann. Da diese Operation nicht immer Sinn ergibt – etwa bei immutablen Datenstrukturen oder wenn ein Iterator zum Beispiel Dateien Zeile für Zeile ausliest – ist sie in der API-Dokumentation als optional gekennzeichnet. Das heißt, dass ein konkreter Iterator keine Löschoperation unterstützen muss und etwa einfach nichts macht oder eine UnsupportedOperationException auslösen könnte.

interface java.util.Iterator<E>
  • default void remove()

    Löscht das zuletzt von next() gelieferte Objekt aus der darunterliegenden Sammlung. Die Operation muss nicht zwingend von Iteratoren angeboten werden und löst, falls nicht anderweitig überschrieben, eine UnsupportedOperationException("remove") aus.

 

Zum Seitenanfang

9.5.2Wer den Iterator liefert Zur vorigen ÜberschriftZur nächsten Überschrift

Iteratoren spielen in Java eine sehr große Rolle und kommen im JDK tausendfach vor. Die Frage ist nur: Woher kommt ein Iterator, sodass sich eine Sammlung ablaufen lässt? Datengeber müssen dazu eine Methode anbieten.

1. Beispiel

iterator() von Path liefert ein Iterator<Path> über die Pfad-Elemente. Es liefert

Iterator<Path> iterator = Paths.get( "/chris/brain/java/8" ).iterator();

while ( iterator.hasNext() )

System.out.println( iterator.next() );

vier Zeilen mit »chris«, »brain«, »java« und »8«.

2. Beispiel

Datenstrukturen wie Listen und Mengen deklarieren ebenfalls eine iterator()-Methode:

Iterator<Integer> iter = new TreeSet<>( Arrays.asList( 4, 2, 9 ) ).iterator();

Die Ausgabe wäre mit der while-Schleife von oben sortiert 2, 4 und 9.

3. Beispiel

Die Klasse Scanner implementiert Iterator<String>, sodass das bekannte Paar von hasNext()/next() über die Tokens laufen kann:

Iterator<String> iterator = new Scanner( "Hund Katze Maus" );

Die Ausgabe besteht mit oberer Schleife aus drei Zeilen.

Im ersten und zweiten Fall ist es also der Aufruf von iterator(), der uns einen Iterator verschafft, im dritten Fall ist es eine Klasse, die selbst ein Iterator mit Konstruktor ist.

 

Zum Seitenanfang

9.5.3Die Schnittstelle Iterable Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Methode iterator(), die Zugang zum Iterator gibt, ist häufig bei Datenstrukturen anzutreffen. Das hat einen Grund. Die Klassen mit iterator()-Methode implementieren eine Schnittstelle java.lang.Iterable, die eine Methode iterator() vorschreibt. Das TreeSet, das wir im Beispiel verwendet haben, implementiert genauso Iterable, wie Path es auch implementiert.

interface java.lang.Iterable<T>
  • Iterator<T> iterator()

    Liefert einen java.util.Iterator, der über alle Elemente vom Typ T iteriert.

Vereinfachtes UML-Diagramm von Iterable und Iterator

Abbildung 9.7Vereinfachtes UML-Diagramm von Iterable und Iterator

 

Zum Seitenanfang

9.5.4Erweitertes for und Iterable Zur vorigen ÜberschriftZur nächsten Überschrift

Bisher haben wir das erweiterte for für kleine Beispiele eingesetzt, in denen es darum ging, ein Array von Elementen abzulaufen:

Listing 9.31com/tutego/insel/iterable/SimpleIterable.java, main()

for ( String s : new String[]{ "Eclipse", "NetBeans", "IntelliJ" } )

System.out.printf( "%s ist toll.%n", s );

Die erweiterte for-Schleife läuft nicht nur Arrays ab, sondern alles, was vom Typ Iterable ist. Da insbesondere viele Datenstrukturklassen diese Schnittstelle implementieren, lässt sich mit dem erweiterten for praktisch durch Ergebnismengen iterieren:

for ( String s : Arrays.asList( "Eclipse", "NetBeans", "IntelliJ" ) )

System.out.printf( "%s ist toll.%n", s );
 

Zum Seitenanfang

9.5.5Interne Iteration Zur vorigen ÜberschriftZur nächsten Überschrift

Die Schnittstelle Iterable bietet zwei Methoden mit Default-Implementierung. Die interessante Methode ist forEach(…), die Methode spliterator() ist an dieser Stelle nicht interessant.

interface java.util.Iterable<T>
  • default void forEach(Consumer<? super T> action)

    Holt den Iterator, läuft über alle Elemente und ruft dann den Konsumenten auf, der das Element übergeben bekommt.

Die Methode forEach(…) realisiert eine so genannte interne Iteration. Das heißt, nicht wir müssen eine Schleife mit dem Paar hasNext()/next() formulieren, sondern das macht forEach(…) für uns.

[zB]Beispiel

Laufe über die ersten Primzahlen und gib sie aus:

Arrays.asList( 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 )

.forEach( e -> System.out.printf( "Primzahl: %d%n", e ) );

Der Aufruf Arrays.asList(…) liefert eine java.util.List und ist Iterable.

 

Zum Seitenanfang

9.5.6Einen eigenen Iterable implementieren * Zur vorigen ÜberschriftZur nächsten Überschrift

Möchten wir selbst rechts neben dem Doppelpunkt vom erweiterten for stehen, müssen wir ein Objekt angeben, dessen Klasse Iterable implementiert und somit eine iterator()-Methode besitzt. iterator() muss einen passenden Iterator zurückgeben. Der wiederum muss die Methoden hasNext() und next() implementieren, um das nächste Element in der Aufzählung anzugeben und die das Ende anzeigen. Zwar schreibt der Iterator auch remove() vor, doch das wird leer implementiert.

Unser Beispiel soll einen praktischen Iterable implementieren, der über Wörter eines Satzes geht. Als grundlegende Implementierung dient der StringTokenizer, der über nextToken() die nächsten Teilfolgen liefert und über hasMoreTokens(), ob weitere Tokens ausgelesen werden können.

Beginnen wir mit dem ersten Teil, der Klasse WordIterable, die erst einmal Iterable implementieren muss, um auf der rechten Seite vom Punkt stehen zu können. Dann muss dieses Exemplar über iterator() einen Iterator zurückgeben, der über alle Wörter läuft. Dieser Iterator kann als eigene Klasse implementiert werden, doch wir implementieren die Klasse WordIterable so, dass sie Iterable und Iterator gleichzeitig verkörpert; daher ist nur ein Exemplar nötig:

Listing 9.32com/tutego/insel/iterable/WordIterable.java

package com.tutego.insel.iterable;



import java.util.*;



class WordIterable implements Iterable<String>, Iterator<String> {



private StringTokenizer st;



public WordIterable( String s ) {

st = new StringTokenizer( s );

}



// Method from interface Iterable



@Override public Iterator<String> iterator() {

return this;

}



// Methods from interface Iterator



@Override public boolean hasNext() {

return st.hasMoreTokens();

}



@Override public String next() {

return st.nextToken();

}

}

Im Beispiel:

Listing 9.33com/tutego/insel/iterable/WordIterableDemo.java, main()

String s = "Am Anfang war das Wort, am Ende die Phrase. (Stanislaw Jerzy Lec)";

for ( String word : new WordIterable(s) )

System.out.println( word );

Die erweiterte for-Schleife baut der (Eclipse-)Compiler intern um zu:

String word;

WordIterable worditerable;

for ( Iterator iterator = (worditerable = new WordIterable(s)).iterator();

iterator.hasNext(); ) {

word = (String) iterator.next();

System.out.println( word );

}

 


Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück

 

 


Copyright © Rheinwerk Verlag GmbH 2017

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de