Java 函数式编程 - 快速指南

函数式编程 - 概述

在函数式编程范式中,应用程序主要使用纯函数编写。纯函数是没有副作用的函数。副作用的一个例子是修改实例级变量,同时从函数返回值。

以下是函数式编程的关键方面。

  • 函数 − 函数是执行特定任务的语句块。函数接受数据、处理数据并返回结果。函数主要是为了支持可重用性的概念而编写的。一旦编写了函数,就可以轻松调用它,而不必一遍又一遍地编写相同的代码。

    函数式编程围绕第一类函数、纯函数和高阶函数展开。

    • 第一类函数是使用第一类实体(如字符串、数字)的函数,这些实体可以作为参数传递、返回或分配给变量。

    • 高阶函数是可以将函数作为参数和/或返回函数的函数。

    • 纯函数是执行时没有副作用的函数。

  • 函数组合 − 在命令式编程中,函数用于组织可执行代码,重点是代码的组织。但在函数式编程中,重点在于函数的组织和组合方式。通常,数据和函数作为参数一起传递并返回。这使编程更强大、更具表现力。

  • 流畅接口 − 流畅接口有助于编写易于编写和理解的表达式。当每个方法返回类型再次被重用时,这些接口有助于链接方法调用。例如 −

LocalDate futureDate = LocalDate.now().plusYears(2).plusDays(3);
  • 急切求值与惰性求值 − 急切求值意味着在遇到表达式时立即求值,而惰性求值是指延迟执行直到满足某些条件。例如,当遇到终端方法时,将评估 Java 8 中的流方法。

  • 持久数据结构

    − 持久数据结构维护其先前版本。每当数据结构状态发生变化时,都会创建结构的新副本,因此数据结构实际上保持不可变。这种不可变集合是线程安全的。
  • 递归 − 可以通过循环或更优雅地使用递归来完成重复计算。如果函数调用自身,则称为递归函数。

  • 并行性 − 没有副作用的函数可以按任何顺序调用,因此是惰性求值的候选。Java 中的函数式编程支持使用提供并行处理的流的并行性。

  • 可选 − Optional 是一个特殊的类,它强制函数永远不应返回 null。它应该使用 Optional 类对象返回值。此返回的对象具有方法 isPresent,可以检查该方法以仅在存在时获取值。

使用 Java 进行函数式编程 - 函数

函数是执行特定任务的语句块。函数接受数据、处理数据并返回结果。函数的编写主要是为了支持可重用性的概念。一旦编写了函数,就可以轻松调用它,而不必一遍又一遍地编写相同的代码。

函数式编程围绕第一类函数、纯函数和高阶函数展开。

  • 第一类函数是使用第一类实体(如字符串、数字)的函数,这些实体可以作为参数传递、返回或分配给变量。

  • 高阶函数是可以将函数作为参数和/或返回函数的函数。

  • 纯函数是执行时没有副作用的函数。

第一类函数

第一类函数可以被视为变量。这意味着它可以作为参数传递给函数,可以由函数返回,也可以分配给变量。 Java 使用 lambda 表达式支持一流函数。lambda 表达式类似于匿名函数。请参阅下面的示例 −

public class FunctionTester {
   public static void main(String[] args) {
      int[] array = {1, 2, 3, 4, 5};
      SquareMaker squareMaker = item -> item * item;
      for(int i = 0; i < array.length; i++){
         System.out.println(squareMaker.square(array[i]));
      }
   }
}
interface SquareMaker {
   int square(int item);
}

输出

1
4
9
16
25

这里我们使用 lambda 表达式创建了 square 函数的实现,并将其分配给变量 squareMaker。

高阶函数

高阶函数要么接受函数作为参数,要么返回函数。在 Java 中,我们可以传递或返回 lambda 表达式来实现这种功能。

import java.util.function.Function;

public class FunctionTester {
   public static void main(String[] args) {
      int[] array = {1, 2, 3, 4, 5};

      Function<Integer, Integer> square = t -> t * t;        
      Function<Integer, Integer> cube = t -> t * t * t;

      for(int i = 0; i < array.length; i++){
         print(square, array[i]);
      }        
      for(int i = 0; i < array.length; i++){
         print(cube, array[i]);
      }
   }

   private static <T, R> void print(Function<T, R> function, T t ) {
      System.out.println(function.apply(t));
   }
}

输出

1
4
9
16
25
1
8
27
64
125

纯函数

纯函数不会修改任何全局变量或修改作为参数传递给它的任何引用。因此它没有副作用。当使用相同参数调用时,它总是返回相同的值。这样的函数非常有用并且是线程安全的。在下面的例子中,sum 是一个纯函数。

public class FunctionTester {
   public static void main(String[] args) {
      int a, b;
      a = 1;
      b = 2;
      System.out.println(sum(a, b));
   }

   private static int sum(int a, int b){
      return a + b;
   }
}

输出

3

使用 Java 进行函数式编程 - 组合

函数组合是指将多个函数组合成单个函数的技术。我们可以将 lambda 表达式组合在一起。Java 使用 Predicate 和 Function 类提供内置支持。以下示例展示了如何使用谓词方法组合两个函数。

import java.util.function.Predicate;
public class FunctionTester {
   public static void main(String[] args) {
      Predicate<String> hasName = text -> text.contains("name");
      Predicate<String> hasPassword = text -> text.contains("password");
      Predicate<String> hasBothNameAndPassword = hasName.and(hasPassword);
      String queryString = "name=test;password=test";
      System.out.println(hasBothNameAndPassword.test(queryString));
   }
}

输出

true

Predicate 提供 and() 和 or() 方法来组合函数。而 Function 提供 compose 和 andThen 方法来组合函数。以下示例展示了如何使用 Function 方法组合两个函数。

import java.util.function.Function;
public class FunctionTester {
   public static void main(String[] args) {
      Function<Integer, Integer> multiply = t -> t *3;
      Function<Integer, Integer> add = t -> t  + 3;
      Function<Integer, Integer> FirstMultiplyThenAdd = multiply.compose(add);
      Function<Integer, Integer> FirstAddThenMultiply = multiply.andThen(add);
      System.out.println(FirstMultiplyThenAdd.apply(3));
      System.out.println(FirstAddThenMultiply.apply(3));
   }
}

输出

18
12

积极求值与惰性求值

积极求值意味着表达式一遇到就求值,而惰性求值是指在需要时才求值。请参阅以下示例以了解这一概念。

import java.util.function.Supplier;

public class FunctionTester {
   public static void main(String[] args) {
      String queryString = "password=test";
      System.out.println(checkInEagerWay(hasName(queryString)
         , hasPassword(queryString)));
      System.out.println(checkInLazyWay(() -> hasName(queryString)
         , () -> hasPassword(queryString)));
   }

   private static boolean hasName(String queryString){
      System.out.println("Checking name: ");
      return queryString.contains("name");
   }

   private static boolean hasPassword(String queryString){
      System.out.println("Checking password: ");
      return queryString.contains("password");
   } 

   private static String checkInEagerWay(boolean result1, boolean result2){
      return (result1 && result2) ? "all conditions passed": "failed.";
   }

   private static String checkInLazyWay(Supplier<Boolean> result1, Supplier<Boolean> result2){
      return (result1.get() && result2.get()) ? "all conditions passed": "failed.";
   }
}

输出

Checking name: 
Checking password: 
failed.
Checking name: 
failed.

此处 checkInEagerWay() 函数首先评估参数,然后执行其语句。而 checkInLazyWay() 执行其语句并根据需要评估参数。由于 && 是短路运算符,checkInLazyWay 仅评估第一个参数(结果为 false),并且根本不评估第二个参数。

持久数据结构

如果数据结构能够将其先前的更新维护为单独的版本,并且每个版本都可以相应地访问和更新,则该数据结构被称为持久数据结构。它使数据结构不可变且线程安全。例如,Java 中的 String 类对象是不可变的。每当我们对字符串进行任何更改时,JVM 都会创建另一个字符串对象,为其分配新值,并将旧值保留为旧字符串对象。

持久数据结构也称为功能数据结构。考虑以下情况 −

非持久方式

public static Person updateAge(Person person, int age){
   person.setAge(age);
   return person;
}

持久化方式

public static Person updateAge(Person pPerson, int age){
   Person person = new Person();
   person.setAge(age);
   return person;
}

Java 函数式编程 - 递归

递归是指在函数中调用同一个函数,直到满足特定条件。它有助于将大问题分解为小问题。递归还使代码更具可读性和表现力。

命令式与递归

以下示例展示了使用这两种技术计算自然数之和。

public class FunctionTester {
   public static void main(String[] args) {
      System.out.println("Sum using imperative way. Sum(5) : " + sum(5));
      System.out.println("Sum using recursive way. Sum(5) : " + sumRecursive(5));
   }

   private static int sum(int n){
      int result = 0;
      for(int i = 1; i <= n; i++){
         result = result + i;
      }
      return result;
   }

   private static int sumRecursive(int n){
      if(n == 1){
         return 1;
      }else{
         return n + sumRecursive(n-1);
      }
   }
}

输出

Sum using imperative way. Sum(5) : 15
Sum using recursive way. Sum(5) : 15

使用递归,我们将 n-1 个自然数与 n 的和的结果相加,得到所需的结果。

尾递归

尾递归表示递归方法调用应该在最后。以下示例显示了使用尾递归打印数字系列。

public class FunctionTester {
   public static void main(String[] args) {
      printUsingTailRecursion(5);
   }

   public static void printUsingTailRecursion(int n){
      if(n == 0) 
         return;
      else
         System.out.println(n);
      printUsingTailRecursion(n-1);
   }
}

输出

5
4
3
2
1

头递归

头递归表示递归方法调用应该在代码的开头。以下示例显示了使用头递归打印数字系列。

public class FunctionTester {
   public static void main(String[] args) {     
      printUsingHeadRecursion(5);
   }

   public static void printUsingHeadRecursion(int n){
      if(n == 0) 
         return;
      else
         printUsingHeadRecursion(n-1); 
      System.out.println(n);
   }
}

输出

1
2
3
4
5

Java 函数式编程 - 并行性

并行性是函数式编程的一个关键概念,其中大任务通过分解成较小的独立任务来完成,然后这些小任务以并行方式完成,然后组合起来给出完整的结果。随着多核处理器的出现,这种技术有助于加快代码执行速度。Java 具有基于线程的编程支持并行处理,但学习起来相当繁琐,并且很难实现没有错误。从 Java 8 开始,流具有并行方法,集合具有 parallelStream() 方法来以并行方式完成任务。请参阅以下示例:

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

public class FunctionTester {
   public static void main(String[] args) {

      Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8 };
      List<Integer> listOfIntegers = new ArrayList<>(Arrays.asList(intArray));

      System.out.println("List using Serial Stream:");
      listOfIntegers
         .stream()
         .forEach(e -> System.out.print(e + " "));
      System.out.println("");

      System.out.println("List using Parallel Stream:");
      listOfIntegers
         .parallelStream()
         .forEach(e -> System.out.print(e + " "));
      System.out.println("");

      System.out.println("List using Another Parallel Stream:");
      listOfIntegers
         .stream()
         .parallel()
         .forEach(e -> System.out.print(e + " "));
      System.out.println("");

      System.out.println("List using Parallel Stream but Ordered:");
      listOfIntegers
         .parallelStream()
         .forEachOrdered(e -> System.out.print(e + " "));
         System.out.println(""); 
   } 
}

输出

List using Serial Stream:
1 2 3 4 5 6 7 8 
List using Parallel Stream:
6 5 8 7 3 4 2 1 
List using Another Parallel Stream:
6 2 1 7 4 3 8 5 
List using Parallel Stream but Ordered:
1 2 3 4 5 6 7 8 

Optional 和 Monad

Monad 是函数式编程的一个关键概念。Monad 是一种设计模式,有助于表示缺失值。它允许包装潜在的空值,允许在其周围进行转换并提取实际值(如果存在)。根据定义,monad 是一组以下参数。

  • 参数化类型 − M<T>

  • 单元函数 − T −> M<T>

  • 绑定操作 − M<T> bind T −> M<U> = M<U>

关键操作

  • 左身份 − 如果函数绑定在特定值的 monad 上,则其结果将与将函数应用于该值时的结果相同。

  • 右身份 − 如果 monad 返回方法与原始值上的 monad 相同。

  • 结合性 − 函数可以以任何顺序应用于 monad。

Optional 类

Java 8 引入了 Optional 类,它是一个 monad。它提供与 monad 等效的操作。例如 return 是一个接受值并返回 monad 的操作。Optional.of() 接受参数并返回 Optional 对象。类似地,bind 是将函数绑定到 monad 以生成 monad 的操作。Optional.flatMap() 是执行 Optional 操作并将结果作为 Optional 返回的方法。

  • 参数化类型 − Optional<T>

  • 单元函数 − Optional.of()

  • 绑定操作 − Optional.flatMap()

示例 − 左标识

以下示例显示了 Optional 类如何遵循左标识规则。

import java.util.Optional;
import java.util.function.Function;

public class FunctionTester {
   public static void main(String[] args) {
      Function<Integer, Optional<Integer>> addOneToX 
         = x −> Optional.of(x + 1);
      System.out.println(Optional.of(5).flatMap(addOneToX)
         .equals(addOneToX.apply(5)));
   } 
}

输出

true

示例 − 正确身份

以下示例显示了可选类如何遵循正确身份规则。

import java.util.Optional;

public class FunctionTester {
   public static void main(String[] args) {
      System.out.println(Optional.of(5).flatMap(Optional::of)
         .equals(Optional.of(5)));
   } 
}

输出

true

示例 - 结合性

以下示例展示了 Optional 类如何遵循结合性规则。

import java.util.Optional;
import java.util.function.Function;

public class FunctionTester {
   public static void main(String[] args) {
      Function<Integer, Optional<Integer>> addOneToX 
         = x −> Optional.of(x + 1);
      Function<Integer, Optional<Integer>> addTwoToX 
         = x −> Optional.of(x + 2);
      Function<Integer, Optional<Integer>> addThreeToX 
         = x −> addOneToX.apply(x).flatMap(addTwoToX);
      Optional.of(5).flatMap(addOneToX).flatMap(addTwoToX)
         .equals(Optional.of(5).flatMap(addThreeToX));
   } 
}

输出

true

Java 函数式编程 - 闭包

闭包是函数与其周围状态的组合。闭包函数通常可以访问外部函数的作用域。在下面给出的示例中,我们创建了一个函数 getWeekDay(String[] days),它返回一个可以返回相当于星期几的文本的函数。这里 getWeekDay() 是一个闭包,它返回一个围绕调用函数作用域的函数。

以下示例显示了闭包的工作原理。

import java.util.function.Function;

public class FunctionTester {
   public static void main(String[] args) {
      String[] weekDays = {"Monday", "Tuesday", "Wednesday", "Thursday",
         "Friday", "Saturday", "Sunday" };
      Function<Integer, String> getIndianWeekDay = getWeekDay(weekDays);
      System.out.println(getIndianWeekDay.apply(6));      
   }

   public static Function<Integer, String> getWeekDay(String[] weekDays){
      return index -> index >= 0 ? weekDays[index % 7] : null;
   }
}

输出

Sunday

Java 函数式编程 - 柯里化

柯里化是一种技术,其中许多参数的函数调用被具有较少参数的多个方法调用所取代。

参见以下等式。

(1 + 2 + 3) = 1 + (2 + 3) = 1 + 5 = 6

就函数而言:

f(1,2,3) = g(1) + h(2 + 3) = 1 + 5 = 6

这种函数级联称为柯里化,调用级联函数必须产生与调用主函数相同的结果。

以下示例显示了柯里化的工作原理。

import java.util.function.Function;

public class FunctionTester {
   public static void main(String[] args) {
      Function<Integer, Function<Integer, Function<Integer, Integer>>> 
         addNumbers = u -> v -> w -> u + v + w;             
      int result = addNumbers.apply(2).apply(3).apply(4);        
      System.out.println(result);
   } 
}

输出

9

使用 Java 进行函数式编程 - Reducing

在函数式编程中,reducing 是一种通过对所有值应用函数将值流缩减为单个结果的技术。从 Java 8 开始,Java 在 Stream 类中提供了 reduce() 函数。流具有内置的 Reduce 方法,如 sum()、average()、count(),它们可作用于流的所有元素并返回单个结果。

以下示例展示了 Reducing 的工作原理。

import java.util.stream.IntStream;

public class FunctionTester {
   public static void main(String[] args) {

      //1 * 2 * 3 * 4 = 24
      int product = IntStream.range(1, 5) 
         .reduce((num1, num2) -> num1 * num2)
         .orElse(-1); 

      //1 + 2 + 3 + 4 = 10
      int sum =  IntStream.range(1, 5).sum();

      System.out.println(product);
      System.out.println(sum);
   } 
}

输出

24
10

函数式编程 - Lambda 表达式

Java 8 引入了 Lambda 表达式,并被誉为 Java 8 的最大特性。Lambda 表达式促进了函数式编程,并大大简化了开发。

语法

Lambda 表达式具有以下语法特征。

参数 -> 表达式主体

以下是 Lambda 表达式的重要特征。

  • 可选类型声明 − 无需声明参数的类型。编译器可以从参数的值推断出相同的类型。

  • 参数周围的可选括号 − 无需在括号中声明单个参数。对于多个参数,必须使用括号。

  • 可选花括号 − 如果表达式主体包含单个语句,则无需在表达式主体中使用花括号。

  • 可选 return 关键字 − 如果主体只有一个表达式来返回值,则编译器会自动返回该值。必须使用花括号来指示表达式返回值。

Lambda 表达式示例

使用您选择的任何编辑器(例如,C:\> JAVA)创建以下 Java 程序。

Java8Tester.java

public class Java8Tester {

   public static void main(String args[]) {
        Java8Tester tester = new Java8Tester();
        
        //带有类型声明
        MathOperation addition = (int a, int b) -> a + b;
        
        //不带有类型声明
        MathOperation subtraction = (a, b) -> a - b;
        
        //带有 return 语句和花括号
        MathOperation multiplication = (int a, int b) -> { return a * b; };
        
        //不带有 return 语句和花括号
        MathOperation division = (int a, int b) -> a / b;
		
		System.out.println("10 + 5 = " + tester.operate(10, 5, 加法));
        System.out.println("10 - 5 = " + tester.operate(10, 5, 减法));
        System.out.println("10 x 5 = " + tester.operate(10, 5, 乘法));
        System.out.println("10 / 5 = " + tester.operate(10, 5, 除法));
        
        //不带括号
        GreetingServicegreetService1 = message ->
        System.out.println("Hello " + message);
        
        //带括号
        GreetingServicegreetService2 = (message) ->
        System.out.println("Hello " + message);
        
        greetService1.sayMessage("Mahesh");
        greetService2.sayMessage("Suresh");
   }
	
   interface MathOperation {
      int operation(int a, int b);
   }
	
   interface GreetingService {
      void sayMessage(String message);
   }
	
   private int operate(int a, int b, MathOperation mathOperation) {
      return mathOperation.operation(a, b);
   }
}

验证结果

使用 javac 编译器编译类,如下所示 −

C:\JAVA>javac Java8Tester.java

现在运行 Java8Tester,如下所示 −

C:\JAVA>java Java8Tester

它应该产生以下输出 −

10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Mahesh
Hello Suresh

以下是上述示例中需要考虑的要点。

  • Lambda 表达式主要用于定义函数式接口的内联实现,即仅具有单一方法的接口。在上述示例中,我们使用了各种类型的 lambda 表达式来定义 MathOperation 接口的操作方法。然后,我们定义了 GreetingService 的 sayMessage 实现。

  • Lambda 表达式消除了对匿名类的需求,并为 Java 提供了非常简单但强大的函数式编程能力。

范围

使用 lambda 表达式,您可以引用任何最终变量或有效最终变量(仅分配一次)。如果第二次为变量赋值,Lambda 表达式会引发编译错误。

范围示例

使用您选择的任何编辑器(例如,C:\> JAVA)创建以下 Java 程序。

Java8Tester.java

public class Java8Tester {

   final static String salutation = "Hello! ";
   
   public static void main(String args[]) {
      GreetingService greetService1 = message -> 
      System.out.println(salutation + message);
      greetService1.sayMessage("Mahesh");
   }
	
   interface GreetingService {
      void sayMessage(String message);
   }
}

验证结果

使用 javac 编译器编译类,如下所示 −

C:\JAVA>javac Java8Tester.java

现在运行 Java8Tester,如下所示 −

C:\JAVA>java Java8Tester

它应该产生以下输出 −

Hello! Mahesh

函数式编程 - default 默认方法

Java 8 在接口中引入了默认方法实现的新概念。添加此功能是为了向后兼容,以便旧接口可用于利用 Java 8 的 lambda 表达式功能。

例如,"List"或"Collection"接口没有"forEach"方法声明。因此,添加此类方法只会破坏集合框架实现。Java 8 引入了默认方法,以便 List/Collection 接口可以具有 forEach 方法的默认实现,而实现这些接口的类不需要实现相同的方法。

语法

public interface vehicle {

   default void print() {
      System.out.println("I am a vehicle!");
   }
}

多个默认值

如果接口中有默认函数,则有可能一个类正在实现两个具有相同默认方法的接口。以下代码解释了如何解决这种歧义。

public interface vehicle {

   default void print() {
      System.out.println("I am a vehicle!");
   }
}

public interface fourWheeler {

   default void print() {
      System.out.println("I am a four wheeler!");
   }
}

第一个解决方案是创建一个自己的方法来覆盖默认实现。

public class car implements vehicle, fourWheeler {

   public void print() {
      System.out.println("I am a four wheeler car vehicle!");
   }
}

第二种解决方案是使用 super 调用指定接口的默认方法。

public class car implements vehicle, fourWheeler {

   default void print() {
      vehicle.super.print();
   }
}

静态默认方法

从 Java 8 开始,接口还可以具有静态辅助方法。

public interface vehicle {

   default void print() {
      System.out.println("I am a vehicle!");
   }
	
   static void blowHorn() {
      System.out.println("Blowing horn!!!");
   }
}

默认方法示例

使用您选择的任何编辑器(例如,C:\> JAVA)创建以下 Java 程序。

Java8Tester.java

public class Java8Tester {

   public static void main(String args[]) {
      Vehicle vehicle = new Car();
      vehicle.print();
   }
}

interface Vehicle {

   default void print() {
      System.out.println("I am a vehicle!");
   }
	
   static void blowHorn() {
      System.out.println("Blowing horn!!!");
   }
}

interface FourWheeler {

   default void print() {
      System.out.println("I am a four wheeler!");
   }
}

class Car implements Vehicle, FourWheeler {

   public void print() {
      Vehicle.super.print();
      FourWheeler.super.print();
      Vehicle.blowHorn();
      System.out.println("I am a car!");
   }
}

验证结果

使用 javac 编译器编译类,如下所示 −

C:\JAVA>javac Java8Tester.java

现在运行 Java8Tester,如下所示 −

C:\JAVA>java Java8Tester

它应该产生以下输出 −

I am a vehicle!
I am a four wheeler!
Blowing horn!!!
I am a car!

函数式编程 - 函数式接口

函数式接口具有单一功能。例如,具有单一方法"compareTo"的 Comparable 接口用于比较目的。Java 8 定义了许多函数式接口,可广泛应用于 lambda 表达式中。以下是 java.util.Function 包中定义的函数式接口列表。

Sr.No. 接口 &描述
1

BiConsumer<T,U>

表示接受两个输入参数但不返回结果的操作。

2

BiFunction<T,U,R>

表示接受两个参数并产生结果的函数。

3

BinaryOperator<T>

表示对两个相同类型的操作数进行的操作,产生与操作数相同类型的结果操作数。

4

BiPredicate<T,U>

表示两个参数的谓词(布尔值函数)。

5

BooleanSupplier

表示布尔值结果的供应商。

6

Consumer<T>

表示接受单个输入参数且不返回任何结果的操作结果。

7

DoubleBinaryOperator

表示对两个双值操作数进行操作并产生双值结果。

8

DoubleConsumer

表示接受单个双值参数但不返回结果的操作。

9

DoubleFunction<R>

表示接受双值参数并产生结果。

10

DoublePredicate

表示一个双值参数的谓词(布尔值函数)。

11

DoubleSupplier

表示双值结果的供应商。

12

DoubleToIntFunction

表示接受双值参数并产生 int 值结果的函数。

13

DoubleToLongFunction

表示接受双值参数并产生长值结果的函数。

14

DoubleUnaryOperator

表示对单个双值操作数执行的操作,产生双值结果。

15

Function<T,R>

表示接受一个参数并产生结果的函数。

16

IntBinaryOperator

表示对两个 int 值操作数进行操作并产生一个 int 值结果。

17

IntConsumer

表示接受单个 int 值参数但不返回任何结果的操作。

18

IntFunction<R>

表示接受 int 值参数并产生结果的函数。

19

IntPredicate

表示一个 int 值参数的谓词(布尔值函数)。

20

IntSupplier

表示 int 值结果的供应商。

21

IntToDoubleFunction

表示接受 int 值参数并产生双值结果的函数。

22

IntToLongFunction

表示接受 int 值参数并产生长值结果的函数。

23

IntUnaryOperator

表示对单个 int 值操作数进行的操作,产生 int 值结果。

24

LongBinaryOperator

表示对两个长值操作数进行操作并产生长值结果。

25

LongConsumer

表示接受单个长值参数但不返回结果的操作。

26

LongFunction<R>

表示接受长值参数并产生结果的函数。

27

LongPredicate

表示一个长值参数的谓词(布尔值函数)。

28

LongSupplier

表示长值结果的供应商。

29

LongToDoubleFunction

表示接受长值参数并产生双值结果的函数结果。

30

LongToIntFunction

表示接受长值参数并产生 int 值结果的函数。

31

LongUnaryOperator

表示对单个长值操作数进行操作,产生长值结果。

32

ObjDoubleConsumer<T>

表示接受对象值和双值的操作参数,并且不返回任何结果。

33

ObjIntConsumer<T>

表示接受对象值和 int 值参数的操作,并且不返回任何结果。

34

ObjLongConsumer<T>

表示接受对象值和长值参数的操作,并且不返回任何结果。

35

谓词<T>

表示一个参数的谓词(布尔值函数)。

36

Supplier<T>

表示结果的提供者。

37

ToDoubleBiFunction<T,U>

表示接受两个参数并产生双值结果的函数。

38

ToDoubleFunction<T>

表示产生双值结果的函数。

39

ToIntBiFunction<T,U>

表示接受两个参数并产生一个 int 值结果。

40

ToIntFunction<T>

表示产生 int 值结果的函数。

41

ToLongBiFunction<T,U>

表示接受两个参数并产生一个 long 值结果的函数。

42

ToLongFunction<T>

表示产生长值结果。

43

UnaryOperator<T>

表示对单个操作数的操作,该操作产生与其操作数相同类型的结果。

函数式接口示例

Predicate <T> 接口是一个函数式接口,带有方法 test(Object) 以返回布尔值。此接口表示测试对象为真或假。

使用您选择的任何编辑器(例如,C:\> JAVA)创建以下 Java 程序。

Java8Tester.java

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
   public static void main(String args[]) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        
        // Predicate<Integer> predicate = n -> true
        // n 作为参数传递给 Predicate 接口的测试方法
        // 无论 n 的值是什么,测试方法始终返回 true。
        
        System.out.println("打印所有数字:");
        
        //将 n 作为参数传递
        eval(list, n->true);
        
        // Predicate<Integer> predicate1 = n -> n%2 == 0
        // n 作为参数传递给 Predicate 接口的测试方法
        // 如果 n%2 为零,测试方法将返回 true
        
        System.out.println("打印偶数:");
        eval(list, n-> n%2 == 0 );
        
        // Predicate<Integer> predicate2 = n -> n > 3
        // n 作为参数传递给 Predicate 接口的测试方法
        // 如果 n 大于 3,则测试方法将返回 true。
        
        System.out.println("打印大于 3 的数字:");
        eval(list, n-> n > 3 );
   }
	
   public static void eval(List<Integer> list, Predicate<Integer> predicate) {
      for(Integer n: list) {
         if(predicate.test(n)) {
            System.out.println(n + " ");
         }
      }
   }
}

这里我们传递了 Predicate 接口,它接受单个输入并返回布尔值。

验证结果

使用 javac 编译器编译类,如下所示 −

C:\JAVA>javac Java8Tester.java

现在运行 Java8Tester,如下所示 −

C:\JAVA>java Java8Tester

它应该产生以下输出 −

Print all numbers:
1
2
3
4
5
6
7
8
9
Print even numbers:
2
4
6
8
Print numbers greater than 3:
4
5
6
7
8
9

函数式编程 - 方法引用

方法引用有助于通过方法名称指向方法。方法引用使用"::"符号描述。方法引用可用于指向以下类型的方法 −

  • 静态方法 - 可以使用 ClassName::Method 名称表示法引用静态方法。

//方法引用 - 静态方式
Factory vehicle_factory_static = VehicleFactory::prepareVehicleInStaticMode;
  • 实例方法 - 可以使用 Object::Method 名称表示法引用实例方法。

//方法引用 - 实例方式
Factory vehicle_factory_instance = new VehicleFactory()::prepareVehicle;

以下示例展示了方法引用在 Java 8 及更高版本中的工作方式。

interface Factory {
   Vehicle prepare(String make, String model, int year);
}

class Vehicle {
   private String make;
   private String model;
   private int year;

   Vehicle(String make, String model, int year){
      this.make = make;
      this.model = model;
      this.year = year;
   }

   public String toString(){
      return "Vehicle[" + make +", " + model + ", " + year+ "]";
   }    
}

class VehicleFactory {
   static Vehicle prepareVehicleInStaticMode(String make, String model, int year){
      return new Vehicle(make, model, year);
   }

   Vehicle prepareVehicle(String make, String model, int year){
      return new Vehicle(make, model, year);
   }
}

public class FunctionTester {    
   public static void main(String[] args) {               
      //Method Reference - Static way
      Factory vehicle_factory_static = VehicleFactory::prepareVehicleInStaticMode;        
      Vehicle carHyundai = vehicle_factory_static.prepare("Hyundai", "Verna", 2018);
      System.out.println(carHyundai);

      //Method Reference - Instance way
      Factory vehicle_factory_instance = new VehicleFactory()::prepareVehicle;        
      Vehicle carTata = vehicle_factory_instance.prepare("Tata", "Harrier", 2019);
      System.out.println(carTata); 
   } 
}

输出

Vehicle[Hyundai, Verna, 2018]
Vehicle[Tata, Harrier, 2019]

构造函数引用

构造函数引用有助于指向构造函数方法。构造函数引用使用"::new"符号访问。

//构造函数引用
Factory vehicle_factory = Vehicle::new;

以下示例展示了构造函数引用在 Java 8 及更高版本中的工作方式。

interface Factory {
   Vehicle prepare(String make, String model, int year);
}

class Vehicle {
   private String make;
   private String model;
   private int year;

   Vehicle(String make, String model, int year){
      this.make = make;
      this.model = model;
      this.year = year;
   }

   public String toString(){
      return "Vehicle[" + make +", " + model + ", " + year+ "]";
   }    
}

public class FunctionTester {
   static Vehicle factory(Factory factoryObj, String make, String model, int year){
      return factoryObj.prepare(make, model, year);
   }

   public static void main(String[] args) {       
      //构造函数引用
      Factory vehicle_factory = Vehicle::new;
      Vehicle carHonda = factory(vehicle_factory, "Honda", "Civic", 2017);
      System.out.println(carHonda);
   } 
}

输出

Vehicle[Honda, Civic, 2017]

Java 函数式编程 - 集合

从 Java 8 开始,Java 引入了流,并将方法添加到集合中以获取流。从集合中检索到流对象后,我们可以对集合应用各种函数式编程方面,如过滤、映射、缩减等。请参阅下面的示例 −

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

public class FunctionTester {    
   public static void main(String[] args) {               
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
        
        //映射
        //获取唯一正方形列表
        List<Integer> squaresList = numbers.stream().map( i -> i*i)
        .distinct().collect(Collectors.toList());
        System.out.println(squaresList);
        
        //文件归档
        //获取非空字符串列表
        List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
        List<String> nonEmptyStrings = strings.stream()
        .filter(string -> !string.isEmpty()).collect(Collectors.toList());
        System.out.println(nonEmptyStrings);
        
        //减少
        int sum = numbers.stream().reduce((num1, num2) -> num1 + num2).orElse(-1);
        System.out.println(sum);
   } 
}

输出

[9, 4, 49, 25]
[abc, bc, efg, abcd, jkl]
25

函数式编程 - 高阶函数

如果函数满足以下任一条件,则该函数被视为高阶函数。

  • 它以一个或多个参数作为函数。

  • 它在执行后返回一个函数。

Java 8 Collections.sort() 方法是高阶函数的理想示例。它接受比较方法作为参数。请参阅下面的示例 −

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

public class FunctionTester {    
   public static void main(String[] args) {               
      List<Integer> numbers = Arrays.asList(3, 4, 6, 7, 9);

      //将函数作为 lambda 表达式传递
      Collections.sort(numbers, (a,b) ->{ return a.compareTo(b); });

      System.out.println(numbers);
      Comparator<Integer> comparator = (a,b) ->{ return a.compareTo(b); };
      Comparator<Integer> reverseComparator = comparator.reversed();
      
      //传递函数
      Collections.sort(numbers, reverseComparator);
      System.out.println(numbers);
   } 
}

输出

[3, 4, 6, 7, 9]
[9, 7, 6, 4, 3]

函数式编程 - 返回函数

高阶函数可以返回一个函数,但如何使用 Java 8 来实现呢?Java 8 提供了可以接受 lambda 表达式的 Function 接口。高阶函数可以返回 lamdba 表达式,因此可以使用此高阶函数创建任意数量的函数。请参见下面的示例 −

import java.util.function.Function;

public class FunctionTester {    
   public static void main(String[] args) {               
      Function<Integer, Integer> addOne = adder(1);
      Function<Integer, Integer> addTwo = adder(2);
      Function<Integer, Integer> addThree = adder(3);

      //result = 4 + 1 = 5
      Integer result = addOne.apply(4);
      System.out.println(result);

      //result = 4 + 2 = 6
      result = addTwo.apply(4);
      System.out.println(result);

      //result = 4 + 3 = 7
      result = addThree.apply(4);
      System.out.println(result);
   }

    //adder - 高阶函数
    //以 lambda 表达式的形式返回一个函数
   	static Function<Integer, Integer> adder(Integer x){
      return y -> y + x;
   }
}

输出

5
6
7

函数式编程 - 第一类函数

如果函数满足以下要求,则称为第一类函数。

  • 它可以作为参数传递给函数。

  • 它可以从函数返回。

  • 它可以分配给变量,然后稍后使用。

Java 8 使用 lambda 表达式支持将函数作为第一类对象。lambda 表达式是函数定义,可以分配给变量,可以作为参数传递,也可以返回。请参见下面的示例−

@FunctionalInterface
interface Calculator<X, Y> {    
   public X compute(X a, Y b);
}

public class FunctionTester {    
    public static void main(String[] args) {
        //将函数赋值给变量
        Calculator<Integer, Integer> calculator = (a,b) -> a * b;
        
        //使用函数变量调用函数
        System.out.println(calculator.compute(2, 3));
        
        //将函数作为参数传递
        printResult(calculator, 2, 3);
        
        //获取函数作为返回结果
        Calculator<Integer, Integer> calculator1 = getCalculator();
        System.out.println(calculator1.compute(2, 3));
    }
    
    //将函数作为参数
    static void printResult(Calculator<Integer, Integer> calculator, Integer a, Integer b){
        System.out.println(calculator.compute(a, b));
    }
    
    //函数作为返回值
    static Calculator<Integer, Integer> getCalculator(){
        Calculator<Integer, Integer> calculator = (a,b) -> a * b;
        return calculator;
    }
}

输出

6
6
6

函数式编程 - 纯函数

如果函数满足以下两个条件,则被视为纯函数 −

  • 对于给定的输入,它总是返回相同的结果,并且其结果完全取决于传递的输入。

  • 它没有副作用,这意味着它不会修改调用者实体的任何状态。

示例 - 纯函数

public class FunctionTester {    
   public static void main(String[] args) {
      int result = sum(2,3);
      System.out.println(result);
  
      result = sum(2,3);
      System.out.println(result);
   }
   static int sum(int a, int b){
      return a + b;
   }
}

输出

5
5

此处 sum() 是一个纯函数,因为当在不同时间传递 2 和 3 作为参数时,它始终返回 5,并且没有副作用。

示例 - 非纯函数

public class FunctionTester {
   private static double valueUsed = 0.0; 
   public static void main(String[] args) {
      double result = randomSum(2.0,3.0);
      System.out.println(result);
      result = randomSum(2.0,3.0);
      System.out.println(result);
   }
   
   static double randomSum(double a, double b){
      valueUsed = Math.random();       
      return valueUsed + a + b;
   }
}

输出

5.919716721877799
5.4830887819586795

此处 randomSum() 是一个非纯函数,因为它在不同时间传递 2 和 3 作为参数时会返回不同的结果,并且还会修改实例变量的状态。

函数式编程 - 类型推断

类型推断是一种技术,通过该技术,编译器可以自动推断传递的参数类型或方法的返回类型。从 Java 8 开始,Lambda 表达式主要使用类型推断。

请参阅下面的示例以了解类型推断。

示例 - 类型推断

public class FunctionTester {

   public static void main(String[] args) {
      Join<Integer,Integer,Integer> sum = (a,b) ->  a + b;
      System.out.println(sum.compute(10,20));

      Join<String, String, String> concat = (a,b) ->  a + b;
      System.out.println(concat.compute("Hello ","World!"));
   }

   interface Join<K,V,R>{
      R compute(K k ,V v);
   }
}

输出

30
Hello World!

lambda 表达式最初将每个参数及其返回类型视为 Object,然后据此推断数据类型。在第一种情况下,推断的类型为 Integer,在第二种情况下,推断的类型为 String。

Lambda 表达式中的异常处理

当函数抛出已检查的表达式时,Lambda 表达式很难编写。请参阅下面的示例 −

import java.net.URLEncoder;
import java.util.Arrays;
import java.util.stream.Collectors;

public class FunctionTester {
   public static void main(String[] args) {
      String url = "www.google.com";
      System.out.println(encodedAddress(url));
   }   

   public static String encodedAddress(String... address) {
      return Arrays.stream(address)
         .map(s -> URLEncoder.encode(s, "UTF-8"))
         .collect(Collectors.joining(","));
   }
}

上述代码无法编译,因为 URLEncode.encode() 会抛出 UnsupportedEncodingException,而 encodeAddress() 方法无法抛出该异常。

一种可能的解决方案是将 URLEncoder.encode() 提取到一个单独的方法中,并在那里处理异常。

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.stream.Collectors;

public class FunctionTester {
   public static void main(String[] args) {
      String url = "www.google.com";       
      System.out.println(encodedAddress(url));
   }   

   public static String encodedString(String s) {
      try {
         URLEncoder.encode(s, "UTF-8");
      }
      catch (UnsupportedEncodingException e) {        
         e.printStackTrace();
      }
      return s;
   }

   public static String encodedAddress(String... address) {
      return Arrays.stream(address)
         .map(s -> encodedString(s))
         .collect(Collectors.joining(","));
   }   
}

但是当我们有多个这样的方法抛出异常时,上述方法并不好。请参阅以下使用功能接口和包装器方法的通用解决方案。

import java.net.URLEncoder;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FunctionTester {
   public static void main(String[] args) {
      String url = "www.google.com";       
      System.out.println(encodedAddress(url));
   }   
   public static String encodedAddress(String... address) {
      return Arrays.stream(address)
         .map(wrapper(s -> URLEncoder.encode(s, "UTF-8")))
         .collect(Collectors.joining(","));
   }

   private static <T, R, E extends Exception> Function<T, R> 
   wrapper(FunctionWithThrows<T, R, E> fe) {
      return arg -> {
         try {
            return fe.apply(arg);
         } catch (Exception e) {
            throw new RuntimeException(e);
         }
      };
   }
}

@FunctionalInterface
interface FunctionWithThrows<T, R, E extends Exception> {
   R apply(T t) throws E;
}

输出

www.google.com

中间方法

Java 8 引入了 Stream API,以促进 Java 中的函数式编程。Stream API 旨在以函数式方式处理对象集合。根据定义,Stream 是可以对其元素进行内部迭代的 Java 组件。

Stream 接口具有终端方法和非终端方法。非终端方法是向流添加侦听器的操作。当调用流的终端方法时,将开始流元素的内部迭代,并为每个元素调用附加到流的侦听器,并通过终端方法收集结果。

此类非终端方法称为中间方法。只能通过称为终端的方法来调用中间方法。以下是 Stream 接口的一些重要中间方法。

  • filter − 根据给定的标准从流中过滤掉不需要的元素。此方法接受一个谓词并将其应用于每个元素。如果谓词函数返回 true,则元素将包含在返回的流中。

  • map − 根据给定的条件将流中的每个元素映射到另一个项目。此方法接受一个函数并将其应用于每个元素。例如,将流中的每个字符串元素转换为大写字符串元素。

  • flatMap − 此方法可用于根据给定的条件将流中的每个元素映射到多个项目。当需要将复杂对象分解为简单对象时使用此方法。例如,将句子列表转换为单词列表。

  • distinct − 如果存在重复项,则返回唯一元素流。

  • limit −返回有限元素流,其中通过向 limit 方法传递数字来指定限制。

示例 - 中级方法

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

public class FunctionTester {    
   public static void main(String[] args) {
      List<String> stringList = 
         Arrays.asList("One", "Two", "Three", "Four", "Five", "One");       

      System.out.println("Example - Filter");
      //过滤长度大于3的字符串。
      Stream<String> longStrings = stringList
         .stream()
         .filter( s -> {return s.length() > 3; });

      //打印字符串
      longStrings.forEach(System.out::println);

      System.out.println("Example - Map");
      //将字符串映射为大写并打印
      stringList
         .stream()
         .map( s -> s.toUpperCase())
         .forEach(System.out::println);

      List<String> sentenceList 
         = Arrays.asList("I am Mahesh.", "I love Java 8 Streams.");     

      System.out.println("Example - flatMap");
      //将字符串映射为大写并打印
      sentenceList
         .stream()
         .flatMap( s -> { return  (Stream<String>) 
            Arrays.asList(s.split(" ")).stream(); })
         .forEach(System.out::println);     

      System.out.println("Example - distinct");
      //将字符串映射为大写并打印
      stringList
         .stream()
         .distinct()
         .forEach(System.out::println);     

      System.out.println("Example - limit");
      //将字符串映射为大写并打印
      stringList
         .stream()
         .limit(2)
         .forEach(System.out::println);        
   }   
}

输出

Example - Filter

Three
Four
Five

Example - Map

ONE
TWO
THREE
FOUR
FIVE
ONE

Example - flatMap

I
am
Mahesh.
I
love
Java
8
Streams.

Example - distinct

One
Two
Three
Four
Five

Example - limit

One
Two

函数式编程 - 终端方法

当在流上调用终端方法时,迭代将在流和任何其他链接流上开始。迭代结束后,将返回终端方法的结果。终端方法不返回流,因此一旦在流上调用终端方法,其非终端方法或中间方法的链接就会停​​止/终止。

通常,终端方法返回单个值,并在流的每个元素上调用。以下是 Stream 接口的一些重要终端方法。每个终端函数都采用谓词函数,启动元素的迭代,并将谓词应用于每个元素。

  • anyMatch − 如果谓词对任何元素返回 true,则返回 true。如果没有元素匹配,则返回 false。

  • allMatch −如果谓词对任何元素返回 false,则返回 false。如果所有元素都匹配,则返回 true。

  • noneMatch − 如果没有元素匹配,则返回 true,否则返回 false。

  • collect − 将每个元素存储到传递的集合中。

  • count − 返回通过中间方法传递的元素数量。

  • findAny − 返回包含任何元素的 Optional 实例,否则返回空实例。

  • findFirst − 返回 Optional 实例下的第一个元素。对于空流,返回空实例。

  • forEach − 将消费者函数应用于每个元素。用于打印流的所有元素。

  • min − 返回流的最小元素。根据传递的比较器谓词比较元素。

  • max − 返回流的最大元素。根据传递的比较器谓词比较元素。

  • reduce − 使用传递的谓词将所有元素减少为单个元素。

  • toArray − 返回流元素的数组。

示例 - 终端方法

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

public class FunctionTester {    
   public static void main(String[] args) {
      List<String> stringList 
         = Arrays.asList("One", "Two", "Three", "Four", "Five", "One");       

      System.out.println("Example - anyMatch");
      //anyMatch-检查是否存在 Two?
      System.out.println("Two is present: " 
         + stringList
         .stream()
         .anyMatch(s -> {return s.contains("Two");}));

      System.out.println("Example - allMatch");
      //allMatch - 检查每个字符串的长度是否大于 2。
      System.out.println("Length > 2: " 
         + stringList
         .stream()
         .allMatch(s -> {return s.length() > 2;}));

      System.out.println("Example - noneMatch");
      //noneMatch - 检查每个字符串的长度是否大于 6。
      System.out.println("Length > 6: " 
         + stringList
         .stream()
         .noneMatch(s -> {return s.length() > 6;}));

      System.out.println("Example - collect");
      System.out.println("List: " 
         + stringList
         .stream()
         .filter(s -> {return s.length() > 3;})
         .collect(Collectors.toList()));

      System.out.println("Example - count");
      System.out.println("Count: " 
         + stringList
         .stream()
         .filter(s -> {return s.length() > 3;})
         .count());

      System.out.println("Example - findAny");
      System.out.println("findAny: " 
         + stringList
         .stream()      
         .findAny().get());

      System.out.println("Example - findFirst");
      System.out.println("findFirst: " 
         + stringList
         .stream()      
         .findFirst().get());

      System.out.println("Example - forEach");
      stringList
         .stream()      
         .forEach(System.out::println);

      System.out.println("Example - min");
      System.out.println("min: " 
         + stringList
         .stream()      
         .min((s1, s2) -> { return s1.compareTo(s2);}));

      System.out.println("Example - max");
      System.out.println("min: " 
         + stringList
         .stream()      
         .max((s1, s2) -> { return s1.compareTo(s2);}));

      System.out.println("Example - reduce");
      System.out.println("reduced: " 
         + stringList
         .stream()      
         .reduce((s1, s2) -> { return s1 + ", "+ s2;})
         .get());
   }   
}

输出

Example - anyMatch

Two is present: true

Example - allMatch

Length > 2: true

Example - noneMatch

Length > 6: true

Example - collect

List: [Three, Four, Five]

Example - count

Count: 3

Example - findAny

findAny: One

Example - findFirst

findFirst: One

Example - forEach

One
Two
Three
Four
Five
One

Example - min

min: Optional[Five]

Example - max

min: Optional[Two]

Example - reduce

reduced: One, Two, Three, Four, Five, One

函数式编程 - 无限流

集合是内存中的数据结构,其中包含集合中的所有元素,我们有外部迭代来遍历集合,而流是一种固定的数据结构,其中元素是按需计算的,并且流具有内置迭代来遍历每个元素。以下示例显示了如何从数组创建流。

int[] numbers = {1, 2, 3, 4};
IntStream numbersFromArray = Arrays.stream(numbers);

上述流的大小固定,由四个数字组成的数组构建,并且不会返回第 4 个元素之后的元素。但是我们可以使用 Stream.iterate() 或 Stream.generate() 方法创建流,该方法可以将 lamdba 表达式传递给流。使用 lamdba 表达式,我们可以传递一个条件,一旦满足该条件,就会给出所需的元素。考虑一下这种情况,我们需要一个 3 的倍数的数字列表。

示例 - 无限流

import java.util.stream.Stream;

public class FunctionTester {    
   public static void main(String[] args) {
      //创建一个 3 的倍数的数字流
      Stream<Integer> numbers = Stream.iterate(0, n -> n + 3);

      numbers
         .limit(10)
         .forEach(System.out::println);
   }   
}

输出

0
3
6
9
12
15
18
21
24
27

为了对无限流进行操作,我们使用了 Stream 接口的 limit() 方法来限制数字的迭代,当它们的计数达到 10 时。

函数式编程 - 固定长度流

我们可以使用多种方法来创建固定长度流。

  • 使用 Stream.of() 方法

  • 使用 Collection.stream() 方法

  • 使用 Stream.builder() 方法

以下示例展示了创建固定长度流的所有上述方法。

示例 - 固定长度流

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

public class FunctionTester {    
   public static void main(String[] args) {

      System.out.println("Stream.of():");
      Stream<Integer> stream  = Stream.of(1, 2, 3, 4, 5);       
      stream.forEach(System.out::println);

      System.out.println("Collection.stream():");
      Integer[] numbers = {1, 2, 3, 4, 5};     
      List<Integer> list = Arrays.asList(numbers);
      list.stream().forEach(System.out::println);

      System.out.println("StreamBuilder.build():");
      Stream.Builder<Integer> streamBuilder = Stream.builder();
      streamBuilder.accept(1);
      streamBuilder.accept(2);
      streamBuilder.accept(3);
      streamBuilder.accept(4);
      streamBuilder.accept(5);
      streamBuilder.build().forEach(System.out::println);    
   }   
}

输出

Stream.of():
1
2
3
4
5
Collection.stream():
1
2
3
4
5
StreamBuilder.build():
1
2
3
4
5