Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Ausnahmen müssen sein
9 Geschachtelte Typen
10 Besondere Typen der Java SE
11 Generics<T>
12 Lambda-Ausdrücke und funktionale Programmierung
13 Architektur, Design und angewandte Objektorientierung
14 Java Platform Module System
15 Die Klassenbibliothek
16 Einführung in die nebenläufige Programmierung
17 Einführung in Datenstrukturen und Algorithmen
18 Einführung in grafische Oberflächen
19 Einführung in Dateien und Datenströme
20 Einführung ins Datenbankmanagement mit JDBC
21 Bits und Bytes, Mathematisches und Geld
22 Testen mit JUnit
23 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Download:

- Listings, ca. 2,7 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

Pfeil10 Besondere Typen der Java SE
Pfeil10.1 Object ist die Mutter aller Klassen
Pfeil10.1.1 Klassenobjekte
Pfeil10.1.2 Objektidentifikation mit toString()
Pfeil10.1.3 Objektgleichwertigkeit mit equals(…) und Identität
Pfeil10.1.4 Klonen eines Objekts mit clone() *
Pfeil10.1.5 Hashwerte über hashCode() liefern *
Pfeil10.1.6 System.identityHashCode(…) und das Problem der nicht eindeutigen Objektverweise *
Pfeil10.1.7 Aufräumen mit finalize() *
Pfeil10.1.8 Synchronisation *
Pfeil10.2 Schwache Referenzen und Cleaner
Pfeil10.3 Die Utility-Klasse java.util.Objects
Pfeil10.3.1 Eingebaute null-Tests für equals(…)/hashCode()
Pfeil10.3.2 Objects.toString(…)
Pfeil10.3.3 null-Prüfungen mit eingebauter Ausnahmebehandlung
Pfeil10.3.4 Tests auf null
Pfeil10.3.5 Indexbezogene Programmargumente auf Korrektheit prüfen
Pfeil10.4 Vergleichen von Objekten und Ordnung herstellen
Pfeil10.4.1 Natürlich geordnet oder nicht?
Pfeil10.4.2 compareXXX()-Methode der Schnittstellen Comparable und Comparator
Pfeil10.4.3 Rückgabewerte kodieren die Ordnung
Pfeil10.4.4 Beispiel-Comparator: den kleinsten Raum einer Sammlung finden
Pfeil10.4.5 Tipps für Comparator und Comparable-Implementierungen
Pfeil10.4.6 Statische und Default-Methoden in Comparator
Pfeil10.5 Wrapper-Klassen und Autoboxing
Pfeil10.5.1 Wrapper-Objekte erzeugen
Pfeil10.5.2 Konvertierungen in eine String-Repräsentation
Pfeil10.5.3 Von einer String-Repräsentation parsen
Pfeil10.5.4 Die Basisklasse Number für numerische Wrapper-Objekte
Pfeil10.5.5 Vergleiche durchführen mit compareXXX(…), compareTo(…), equals(…) und Hashwerten
Pfeil10.5.6 Statische Reduzierungsmethoden in Wrapper-Klassen
Pfeil10.5.7 Konstanten für die Größe eines primitiven Typs
Pfeil10.5.8 Behandeln von vorzeichenlosen Zahlen *
Pfeil10.5.9 Die Klasse Integer
Pfeil10.5.10 Die Klassen Double und Float für Fließkommazahlen
Pfeil10.5.11 Die Long-Klasse
Pfeil10.5.12 Die Boolean-Klasse
Pfeil10.5.13 Autoboxing: Boxing und Unboxing
Pfeil10.6 Iterator, Iterable *
Pfeil10.6.1 Die Schnittstelle Iterator
Pfeil10.6.2 Wer den Iterator liefert
Pfeil10.6.3 Die Schnittstelle Iterable
Pfeil10.6.4 Erweitertes for und Iterable
Pfeil10.6.5 Interne Iteration
Pfeil10.6.6 Eine eigene Iterable implementieren *
Pfeil10.7 Die Spezial-Oberklasse Enum
Pfeil10.7.1 Methoden auf Enum-Objekten
Pfeil10.7.2 Aufzählungen mit eigenen Methoden und Initialisierern *
Pfeil10.7.3 enum mit eigenen Konstruktoren *
Pfeil10.8 Annotationen in der Java SE
Pfeil10.8.1 Orte für Annotationen
Pfeil10.8.2 Annotationstypen aus java.lang
Pfeil10.8.3 @Deprecated
Pfeil10.8.4 Annotationen mit zusätzlichen Informationen
Pfeil10.8.5 @SuppressWarnings
Pfeil10.9 Zum Weiterlesen
 

Zum Seitenanfang

10.6    Iterator, 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 Sammlung zeichnet sich hauptsächlich durch Methoden aus, die Daten hinzufügen, wieder entfernen und das Vorhandensein von Elementen prüfen. 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

10.6.1    Die 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 10.9    Zwei 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. Wahlfreien Zugriff haben wir nicht. Ü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 und setzt die Position weiter. Es gibt eine 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.[ 203 ](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 herspringen. Ein Iterator lässt sich an einem Datenstrom veranschaulichen: 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. Grundsätzlich entscheidet die Implementierung der Datenstruktur und des Iterators, in welcher Reihenfolge die Elemente herausgegeben werden. Sind die Daten sortiert, so wird auch der Iterator die Ordnung achten. (Die Dokumentation beschreibt die Details.)

[»]  Hinweis

In Java steht der Iterator nicht auf einem Element, sondern zwischen Elementen. Die Methode hasNext() sagt, ob es ein nächstes Element gibt, und next() liefert es und setzt die interne Position weiter. Es gibt kein Konzept vom aktuellen Element, das immer wieder erfragbar ist; next() ist zustandsbehaftet. Zu Beginn der Iteration steht der Iterator vor dem ersten Element.

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 10.31    java.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 folgt in Kapitel 12, »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 letzten 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, z. B. aus 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

10.6.2    Wer 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. Der Ausdruck

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

while ( iterator.hasNext() )

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

liefert vier Zeilen mit »chris«, »brain«, »java« und »9«.

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 der oben gezeigten 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

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

Eine Methode iterator(), die einen Iterator liefert, ist häufig bei Datenstrukturen anzutreffen. Das hat einen Grund: Die Klassen mit iterator()-Methode implementieren eine Schnittstelle java.lang.Iterable, und die schreibt die Operation iterator() vor. 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.

UML-Diagramm von »Iterable« und »Iterator«

Abbildung 10.8    UML-Diagramm von »Iterable« und »Iterator«

 

Zum Seitenanfang

10.6.4    Erweitertes 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 10.32    src/main/java/com/tutego/insel/iterable/SimpleIterable.java, main()

for ( String s : new String[]{ "T. Noah", "S. Colbert", "J. Oliver" } )

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( "T. Noah", "S. Colbert", "J. Oliver" ) )

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

Zum Seitenanfang

10.6.5    Interne 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 dagegen 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 sogenannte 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

10.6.6    Eine eigene Iterable implementieren * Zur vorigen ÜberschriftZur nächsten Überschrift

Damit unsere eigenen Objekte rechts hinter dem Doppelpunkt vom erweiterten for stehen können, muss die entsprechende Klasse die Schnittstelle Iterable implementieren und somit eine iterator()-Methode anbieten. iterator() muss einen passenden Iterator zurückgeben. Der wiederum muss die Methoden hasNext() und next() implementieren, die das nächste Element in der Aufzählung angeben und das Ende anzeigen. Zwar schreibt der Iterator auch remove() vor, doch das wird leer implementiert.

Unser Beispiel soll aus einer Klasse bestehen, die Iterable implementiert, und Wörter eines Satzes zerlegen kann. Als grundlegende Implementierung dient der StringTokenizer, der über nextToken() die nächsten Teilfolgen liefert und über hasMoreTokens() meldet, 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. Der Nachteil ist, dass es für ein WordIterable nicht mehrere unterschiedliche Iterator-Exemplare geben kann.

Listing 10.33    src/main/java/com/tutego/insel/iterable/WordIterable.java, Ausschnitt

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() {

if ( hasNext() )

return st.nextToken();

throw new NoSuchElementException();

}

}

Im Beispiel:

Listing 10.34    src/main/java/com/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 Java-Compiler intern um zu:

{

String word; Iterator<String> iterator = new WordIterable(s ).iterator();

while ( iterator.hasNext() ) {

word = iterator.next();

System.out.println( word );

}

word = null;

iterator = null;

}

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Objektorientierte Programmierung

Objektorientierte Programmierung




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2021

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



Cookie-Einstellungen ändern