codestory

Die Anleitung zu Java Stream

  1. Stream
  2. Stream.filter(Predicate)
  3. Stream.sorted(Comparator)
  4. Stream.map(Function)
  5. Stream.flatMap(Function)

1. Stream

Java 8 führt ein neues Konzept namens Stream ein. Wenn Sie zum ersten Mal über Stream API lesen, können Sie verwirren, denn der Name InputStream und OutputStream ähnelt, Java 8 Stream jedoch etwas ganz ändert. Stream ist eine Monade, daher spielt es eine wichtige Rolle, um funktionale Programmierung in Java zu bringen.
Vor dem Beginn des Artikel schlage ich Sie vor, den Konzept functional interface und einige üblichen functional interface wie Supplier, Consumer, Predicate zu lernen. Unten sind das meine Artikels:
Im funktionelle Programmierung ist eine Monade eine Struktur, die eine Berechnung (computation) darstellt, die eine Reihe von miteinander verbundenen Schritten erfordert. Zum Einfachen sehen Sie das Beispiel über die folgende Monade:
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");

myList
      .stream() // (1)  return a Stream
      .filter(s -> s.startsWith("c")) // (2)  return a new Stream
      .map(String::toUpperCase)  // (3)  return a new Stream
      .sorted()   // (4)  return a new Stream
      .forEach(System.out::println); // (5)
Output:
C1
C2
  • Ein Stream aus einem Objekt List erstellen.
  • Ein neues Stream aus einem vorhandenen Stream erstellen und fasst die Elemente um, die mit "c" beginnt.
  • Ein neues Stream aus einem vorhandenen Stream erstellen und alle Elementen wird zum Großbuchstabe umgewandelt.
  • Ein neues Stream aus einem vorhandenen Stream erstellen, indem die Elemente reorganisiert werden.
  • Die Elemente von letzten Stream ausdrucken.
In diesem obengemeinten Beispiel sind die Schritten von (2) zum (4) die mitleren Aktionen (intermediate operation) denn sie geben ein Objekt Stream zurück. Deshalb können Sie eine andere Methodes von Stream aufrufen ohne es mit einem Komma zu enden.
Die Terminal Operaton ist eine Methode, die void oder ein anderes Typ als Stream zurückgibt. In diesem Beispiel ist der Schritt 5 eine Terminal Operation denn die Methode Stream.forEach gibt void zurück.
Danach sind das die Eigenschaften und Vorteile von Java 8 Stream:
  • Nicht lagern. Stream ist keine Datenstruktur, sondern nur eine Ansicht der Datenquelle (Es kann ein Array, eine Liste oder ein I/O Channel,usw sein.).
  • Ein Stream ist eine natürliche Funktion. Mit der Änderungen für Stream wird die Datenquelle nicht geändert. Z.B: Durch das Filtern eines Stream werden keine Elemente gelöscht, sondern ein neuer Stream erstellt, der die gefilterten Elemente enthält.
  • Faule Ausführung. Die Aktivitäten auf Stream werden nicht sofort ausgeführt. Sie werden nur ausgeführt, wenn der Benutzer die Ergebnisse wirklich benötigt.
  • Verbrauchbar (Consumable). Auf die Elemente von Stream wird während der Lebensdauer von Stream nur einmal zugegriffen. Nach der Genehmigung wird Stream wie einem Iterator deaktiviert. Sie müssen einen neuen Stream neu erstellen wenn Sie Stream wieder ansehen möchten.
Sehen Sie die folgende Abbildung um besser zu verstehen, wie Stream funktioniert.
  • Erstellen Sie einen stream aus einer Sammlung (collection).
  • Filtern Sie die andere Farbe als rot.
  • Malen Sie die Dreiecke rosa.
  • Filtern Sie die andere Formen als Quadrate.
  • Berechnen Sie die Gesamtfläche.
Die Klasse Employee nehmen an die einigen Beispiele in diesem Artikel teil:
Employee.java
package org.o7planning.stream.ex;

public class Employee {

    private String name;
    private float salary;
    private String gender; // "M", "F"

    public Employee(String name, float salary, String gender) {
        this.name = name;
        this.salary = salary;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public float getSalary() {
        return salary;
    }

    public String getGender() {
        return gender;
    }

    public boolean isFemale() {
        return "F".equals(this.getGender());
    }
}

2. Stream.filter(Predicate)

Geben Sie einen Stream zurück, einschließend die Elementen von Stream entsprechen der angegebenen Predicate .
Stream<T> filter(Predicate<? super T> predicate)
Z.B: Aus einer Liste der Mitarbeiter (Employee) drucken Sie die Liste der weiblichen Mitarbeiterinnen, deren Gehalt größer als 2500 ist.
Stream_filter_ex1.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Stream_filter_ex1 {

    public static void main(String[] args) {

        Employee john = new Employee("John P.", 1500, "M");
        Employee sarah = new Employee("Sarah M.", 2000, "F");
        Employee charles = new Employee("Charles B.", 1700, "M");
        Employee mary = new Employee("Mary T.", 5000, "F");
        Employee sophia = new Employee("Sophia B.", 7000, "F");

        List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
        
        // Employee is Female and salary > 2500
        Predicate<Employee> predicate = e -> e.isFemale() && e.getSalary() > 2500;

        employees //
             .stream() //
             .filter(predicate) //
             .forEach(e -> System.out.println(e.getName()+ " : " + e.getSalary()));
    }
}
Output:
Mary T. : 5000.0
Sophia B. : 7000.0
Wenn eine nicht-statische Methode ohne Parameter einen Wert boolean zurückgibt, gilt ihre Referenz als eine Predicate. (Sehen Sie meinen Artikel über Java Predicate mehr).
Z.B: Erstellen Sie eine Predicate aus der Methode-Referenz:
Predicate<Employee> p = Employee::isFemale;
 
// Same as:
 
Predicate<Employee> p = employee -> employee.isFemale();
Stream_filter_ex2.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;

public class Stream_filter_ex2 {

    public static void main(String[] args) {
        Employee john = new Employee("John P.", 1500, "M");
        Employee sarah = new Employee("Sarah M.", 2000, "F");
        Employee charles = new Employee("Charles B.", 1700, "M");
        Employee mary = new Employee("Mary T.", 5000, "F");
        Employee sophia = new Employee("Sophia B.", 7000, "F");

        List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);

        employees //
             .stream() //
             .filter(Employee::isFemale) //
             .forEach(e -> System.out.println(e.getName()+ " : " + e.getSalary()));
    }
}
Output:
Sarah M. : 2000.0
Mary T. : 5000.0
Sophia B. : 7000.0

3. Stream.sorted(Comparator)

Geben Sie einen Stream zurück, einschließend die Elemente dieses Stream, die nach dem angegebenen Comparator zugeordnet werden.
Stream<T> sorted(Comparator<? super T> comparator)
  • Die Anleitung zu Java Comparator
Z.B: Ordnen Sie die Mitarbeiter nach der steigenden Reihenfolge zu:
Stream_sort_ex1.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;

public class Stream_sort_ex1 {

    public static void main(String[] args) {
        Employee john = new Employee("John P.", 1500, "M");
        Employee sarah = new Employee("Sarah M.", 2000, "F");
        Employee charles = new Employee("Charles B.", 1700, "M");
        Employee mary = new Employee("Mary T.", 5000, "F");
        Employee sophia = new Employee("Sophia B.", 7000, "F");

        List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);

        employees //
             .stream() //
             .sorted (
                 (e1,e2) -> (int) (e1.getSalary() - e2.getSalary())
              ) //
             .forEach(e -> System.out.println(e.getSalary() + " : " + e.getName()));
    }
}
Output:
1500.0 : John P.
1700.0 : Charles B.
2000.0 : Sarah M.
5000.0 : Mary T.
7000.0 : Sophia B.
Z.B: Ordnen Sie die Mitarbeiter nach dem Geschecht (Gender) und Gehalt:
Stream_sort_ex2.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;

public class Stream_sort_ex2 {

    public static void main(String[] args) {
        Employee john = new Employee("John P.", 1500, "M");
        Employee sarah = new Employee("Sarah M.", 2000, "F");
        Employee charles = new Employee("Charles B.", 1700, "M");
        Employee mary = new Employee("Mary T.", 5000, "F");
        Employee sophia = new Employee("Sophia B.", 7000, "F");

        List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);

        employees //
             .stream() //
             .sorted (
                 (e1,e2) -> {
                     int v = e1.getGender().compareTo(e2.getGender());
                     if(v == 0) {
                         v = (int) (e1.getSalary() - e2.getSalary());
                     }
                     return v;
                 }
              ) //
             .forEach(e -> System.out.println(e.getGender()+ " : "+ e.getSalary() + " : " + e.getName()));
    }
}
Output:
F : 2000.0 : Sarah M.
F : 5000.0 : Mary T.
F : 7000.0 : Sophia B.
M : 1500.0 : John P.
M : 1700.0 : Charles B.

4. Stream.map(Function)

Geben Sie einen neuen Stream zurück, einschließend die Ergebnisse der angegebenen Function für die Elemente von Stream.
<R> Stream<R> map(Function<? super T,? extends R> mapper)
Z.B: Wandeln Sie eine Liste von String zur Großbuchstabe um (uppercase).
Stream_map_ex1.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Stream_map_ex1 {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "d", "e");
        
        List<String> newList = list //
                    .stream() // a Stream
                    .map(s -> s.toUpperCase()) // a new Stream
                    .collect(Collectors.toList()); // Stream => List

        System.out.println(list); // [a, b, c, d, e]
        System.out.println(newList); // [A, B, C, D, E]
    }
}
Wenn eine nicht-statische Methode (non-static method) ohne Parameter und einen Wert zurückgibt, wird ihr Referenz als eine Function betrachten. (Sehen Sie mehr die Erklärung in meinem Artikel über Java Function).
// Create a Function from a method reference:
Function<String, String> f1 = String::toUpperCase;
// Same as:
Function<String, String> f2 = s -> s.toUpperCase();
Stream_map_ex2.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Stream_map_ex2 {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "d", "e");
        
        List<String> newList = list //
                    .stream() // a Stream
                    .map(String::toUpperCase) // a new Stream
                    .collect(Collectors.toList()); // Stream => List  

        System.out.println(list); // [a, b, c, d, e]
        System.out.println(newList); // [A, B, C, D, E]
    }
}
Z.B: Doppelte Gehaltserhöhung für jeden Mitarbeiter in einer Liste:
Stream_map_ex3.java
package org.o7planning.stream.ex;

import java.util.Arrays;
import java.util.List;

public class Stream_map_ex3 {

    public static void main(String[] args) {

        Employee john = new Employee("John P.", 1500, "M");
        Employee sarah = new Employee("Sarah M.", 2000, "F");
        Employee charles = new Employee("Charles B.", 1700, "M");
        Employee mary = new Employee("Mary T.", 5000, "F");
        Employee sophia = new Employee("Sophia B.", 7000, "F");

        List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
        
        employees //
             .stream() // a Stream.
             .map((e) -> new Employee(e.getName(), e.getSalary()* 2, e.getGender())) // a new Stream.
             .forEach(c -> System.out.println(c.getName()+ " : " + c.getSalary()));
    }  
}
Output:
John P. : 3000.0
Sarah M. : 4000.0
Charles B. : 3400.0
Mary T. : 10000.0
Sophia B. : 14000.0

5. Stream.flatMap(Function)

public class Stream<T> {

    <R> Stream<R> flatMap​(Function<? super T,​? extends Stream<? extends R>> mapper);
    
    // .....
}
Die Methode Stream.flatMap ist eine ziemlich nutzliche Methode und hat viel zu sagen. Deshalb schreibe ich in einem eigenen Artikel:
  • Java Stream.flatMap

Java Grundlagen

Show More