Java8 Lambda 表达式、Stream

为什么要学习Java8?我想根据我做项目的亲身体会来说下:

  • 性能问题:项目中涉及到矩阵等相关数学计算的过程中(20万条测试数据),jdk8:4秒,jdk7:40+秒
  • 跳槽后,接手新公司的项目,发现大量使用Java8的新特性,导致我花费更多的时间去熟悉项目
  • 未来的面试需要

首先说下最吸引人的Lambda 表达式:

Lambda 表达式,也可称为闭包,lambda表达式替换匿名类,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

看一个例子:

// Java 8之前:
new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Before Java8, too much code for too little to do");
    }
}).start();

//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
public class Java8Test {
   public static void main(String args[]){
      Java8Test tester = new Java8Test();
        
      // 类型声明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括号
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括号
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   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);
   }
}

运行结果:

10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

熟悉8的语法需要常用,多看些例子;

@Test
public void mapTest() {
    List<Double> cost = Arrays.asList(10.0, 20.0, 30.0);
    cost.stream().map(x -> x + x*0.05).forEach(x -> System.out.println(x));
}

输出:
10.5
21.0
31.5

map的作用是将一个对象变换为另外一个
forEach的作用就是遍历
@Test
public void filterTest() {
    List<Double> cost = Arrays.asList(10.0, 20.0,30.0,40.0);
    List<Double> filteredCost = cost.stream().filter(x -> x > 25.0).collect(Collectors.toList());
    filteredCost.forEach(x -> System.out.println(x));

}

filter就是按照条件过滤list

函数式接口Predicate:

    public static void filterTest(List<String> languages, Predicate<String> condition) {
        languages.stream().filter(x -> condition.test(x)).forEach(x -> System.out.println(x + " "));
    }

    public static void main(String[] args) {
        List<String> languages = Arrays.asList("Java","Python","scala","Shell","R");

        System.out.println("Language starts with J: ");
        filterTest(languages,x -> x.startsWith("J"));

        System.out.println("\nLanguage ends with a: ");
        filterTest(languages,x -> x.endsWith("a"));

        System.out.println("\nAll languages: ");
        filterTest(languages,x -> true);

        System.out.println("\nNo languages: ");
        filterTest(languages,x -> false);

        System.out.println("\nLanguage length bigger three: ");
        filterTest(languages,x -> x.length() > 4);
    }


输出结果:
Language starts with J: 
Java 

Language ends with a: 
Java 
scala 

All languages: 
Java 
Python 
scala 
Shell 
R 

No languages: 

Language length bigger three: 
Python 
scala 
Shell 
// 更好的办法
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

可以看到,Stream API的过滤方法也接受一个Predicate,这意味着可以将我们定制的 filter() 方法替换成写在里面的内联代码,这就是lambda表达式的魔力。另外,Predicate接口也允许进行多重条件的测试,下个例子将要讲到。

// 甚至可以用and()、or()和xor()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));

类似地,也可以使用 or() 和 xor() 方法。着重介绍了如下要点:可按需要将 Predicate 作为单独条件然后将其合并起来使用。简而言之,你可以以传统Java命令方式使用 Predicate 接口,也可以充分利用lambda表达式达到事半功倍的效果。

事件处理:

// Java 8之前:
JButton show =  new JButton("Show");
show.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
    System.out.println("Event handling without lambda expression is boring");
    }
});

// Java 8方式:
show.addActionListener((e) -> {
    System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});

迭代处理(遍历):

// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
    System.out.println(feature);
}

// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
 
// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
// 看起来像C++的作用域解析运算符
features.forEach(System.out::println);

lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式可以换为方法引用,因为这仅是一个参数相同的简单方法调用:

list.forEach(n -> System.out.println(n)); 
list.forEach(System.out::println);  // 使用方法引用

然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,如下所示:
 

list.forEach((String s) -> System.out.println("*" + s + "*"));

Stream:流处理

基础操作:

forEach:迭代流中的每个数据
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map: 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
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());

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.stream().filter(string -> string.isEmpty()).count();

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

高级操作:

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

参考复制链接:
https://www.runoob.com/java/java8-lambda-expressions.html
https://blog.csdn.net/bitcarmanlee/article/details/70195403
https://www.cnblogs.com/coprince/p/8692972.html

发布了142 篇原创文章 · 获赞 345 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/zhengchao1991/article/details/99995950