Java 8 - Listenverarbeitung mit Streams

Ideen und Lösungen zu Programmierproblemen sowie nützliche Tipps für effizienteres Arbeiten mit Java

Java 8 - Listenverarbeitung mit Streams

Postby kottmair » Fri Apr 04, 2014 11:06 am

Java 8 bietet neue Möglichkeiten zum Arbeiten mit Listen und Arrays. Mit den sogenannten Streams können auf Listen mehrere verkettete Operationen angewandt werden. Code, mit dem alle Zeilen aus einer Datei, die länger als 80 Zeichen sind, ausgegeben werden, hätte unter Java 7 noch so ausgesehen:

Code: Select all
        List<String> lines = Files.readAllLines(new File("test.txt").toPath());
        for (String line : lines) {
            if (line.length() > 80) {
                System.out.println(line);
            }
        }


In Java 8 kann die Zeilenliste mit der Funktion stream() direkt in einen Stream umgewandelt werden. Dieser Stream wird anschließend mit filter() gefiltert und aus dem Ergebnis dieses Verarbeitungsschritts wird jede übrig gebliebene Zeile mit forEach() ausgegeben:

Code: Select all
        lines.stream()
             .filter(line -> line.length() > 80)
             .forEach(line -> System.out.println(line));


stream() wandelt die Zeilenliste lines in einen Stream um.
filter(variablenname -> boolesche Bedingung) durchläuft jedes Element des Streams (für die einzelnen Elemente wird ein beliebiger Variablenname vergeben; die Typisierung (String) ist überflüssig, da der Compiler dies aus dem Kontext erkennt) und behält jedes Element, für das der angegebene boolesche Code true zurückgibt.
forEach(variablenname -> Programmcode) durchläuft jedes verbliebene Element (auf das mit dem angegebenen Variablennamen innerhalb des Codes zugegriffen werden kann) und führt jeweils den definierten Programmcode aus.

Streams besitzen neben filter() und forEach() noch eine ganze Reihe weiterer Funktionen und Methoden. Intermediäre Funktionen wie filter(), sort(), limit() oder skip() liefern ihrerseits wieder einen Stream zurück, der weiter verarbeitet werden kann; diese Methoden können also verkettet werden. Am Ende einer Verkettung steht eine terminale Operation wie forEach(), die über jedes verbliebene Element iteriert oder eine Funktion wie min(), max() oder count(), die einen Wert zurückgibt.

Die Anzahl der Zeilen, die länger als 80 Zeichen sind, gibt beispielsweise dieser Aufruf zurück:

Code: Select all
        long anzahl = lines.parallelStream()
                           .filter(line -> line.length() > 80).count();


Hier wurde auch eine weitere Besonderheit von Streams genutzt: parallelStream() gibt einen parallelisierten Stream zurück. Es muss also kein besonderer Code mehr für die Parallelverarbeitung (Multithreading) geschrieben werden, das übernimmt die Java-VM (der Compiler) selbständig. Jeder Stream kann über die Funktion parallel() ab jedem beliebigen Verarbeitungsschritt parallelisiert werden; analog zu obigem Beispiel könnte man auch schreiben:

Code: Select all
        long anzahl = lines.stream()
                           .parallel()
                           .filter(line -> line.length() > 80).count();


Die Schnittstelle Collections (und damit sämtliche Listen) enthält die Funktionen stream() und parallelStream() zur Erzeugung von Streams. Zur Umwandlung eines Arrays in einen Stream existiert die Funktion Arrays.stream(). Als Beispiel erzeugen wir aus einem Array mit Wochentagen einen Stream, der sortiert und ausgegeben wird:

Code: Select all
        String[] arrDays = new String[] {"Montag", "Dienstag", "Mittwoch",
                               "Donnerstag", "Freitag", "Samstag", "Sonntag"};
        Arrays.stream(arrDays).sorted().forEach(day -> System.out.println(day));


Achtung: Würde an dieser Stelle mit einem parallelen Stream gearbeitet, wäre die Ausgabe nicht mehr sortiert, da dann jedes Element einzeln parallel verarbeitet würde und die Reihenfolge der Bildschirmausgabe nicht mehr kontrolliert werden könnte.
Auch die Rückumwandlung des sortierten Streams in ein Array ist möglich:

Code: Select all
        String[] sorted = Arrays.stream(arrDays).sorted().toArray(String[]::new);


Als letztes Beispiel sei hier noch dargestellt, wie man mittels eines parallelen Streams alle Dateien eines Verzeichnisses unter Berücksichtigung von Multithreading in ein anderes Verzeichnis kopieren kann:

Code: Select all
        File source = new File("C:\\Dokumente");
        File target = new File("C:\\Temp");
        Stream<File> files = Arrays.stream(source.listFiles());
        files.parallel().forEach(file -> {
            try {
                Files.copy(file.toPath(),
                           target.toPath().resolve(file.getName()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
kottmair
Site Admin
 
Posts: 8
Joined: Fri Sep 14, 2012 8:50 am

Return to Tipps und Tricks

Who is online

Users browsing this forum: No registered users and 0 guests

cron