Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda表达式还增强了集合库。 Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及java.util.stream 包。 流(stream)就如同迭代器(iterator),但附加了许多额外的功能。 总的来说,lambda表达式和 stream 是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。 在本文中,我们将从简单到复杂的示例中见认识lambda表达式和stream的强悍。
lambda 表达式的语法格式如下:
(parameters) -> expression 或 (parameters) ->{ statements; }
以下是lambda表达式的重要特征:
// 1. 不需要参数,返回值为 5 () -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值 (x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和 (int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> System.out.print(s)
现在,我们已经知道什么是lambda表达式,让我们先从一些基本的例子开始。 在本节中,我们将看到lambda表达式如何影响我们编码的方式。 假设有一个玩家List ,程序员可以使用 for 语句 ("for 循环")来遍历,在Java SE 8中可以转换为另一种形式:
// 字符串数组 String[] atp = {"Brandon", "Mason", "Elijah"}; List<String> players = Arrays.asList(atp);
// java8之前 for (String player : players) { System.out.println(player + "; "); } // 使用 lambda 表达式以及函数操作 players.forEach((player) -> System.out.println(player + "; ")); // 在 Java 8 中使用双冒号操作符 players.forEach(System.out::println);
lambda表达式可以将我们的代码缩减到一行
在Java中,Comparator 类被用来排序集合。 在下面的例子中,我们将根据球员的 name, surname, name 长度 以及最后一个字母。 使用匿名内部类来排序,然后再使用lambda表达式精简我们的代码。 在第一个例子中,我们将根据name来排序list。 使用旧的方式,代码如下所示:
// 使用匿名内部类根据 长度 排序 players players.sort(new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } });
players.sort((String s1, String s2) -> (s1.length() - s2.length()));
System.out.println(players);
就是这样,简洁又直观。 在下面我们将探索更多lambdas的能力,并将其与 stream 结合起来使用。
Stream是对集合的包装,通常和lambda一起使用。 使用lambdas可以支持许多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。 同样,Stream使用懒运算,他们并不会真正地读取所有数据,遇到像getFirst() 这样的方法就会结束链式语法。 在接下来的例子中,我们将探索lambdas和streams 能做什么。 我们创建了一个User类并使用这个类来添加一些数据到list中,将用于进一步流操作。 User 只是一个简单的POJO类:
package com.li.springbootthymeleaf;
/** * @Classname User * @Description 用户实体类 * @Author 李号东 lihaodongmail@163.com * @Date 2019-04-29 13:54 * @Version 1.0 */public class User {
private String name,age;
public User(String name,String age){ this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getAge() { return age; }
public void setAge(String age) { this.age = age; }}
接下来,我们将创建两个list,都用来存放User对象:
List<User> usersOne = new ArrayList<User>(){ { add(new User("小王",18)); add(new User("小李",18)); add(new User("小刘",18)); add(new User("小张",18)); } };
List<User> usersTwo = new ArrayList<User>(){ { add(new User("小高",18)); add(new User("小陈",18)); add(new User("小沈",18)); add(new User("小罗",18)); } };
现在我们使用forEach方法来迭代输出内容:
usersOne.forEach(user -> {System.out.printf("%s %s; ",user.getName(), user.getAge());});usersTwo.forEach(user -> {System.out.printf("%s %s; ",user.getName(), user.getAge());});
我们同样使用forEach方法,增加用户的年龄:
System.out.println("所有用户年龄加5岁"); Consumer<User> addAge = e -> e.setAge(e.getAge() + 5); usersOne.forEach(addAge); usersTwo.forEach(addAge); System.out.println("加完后年龄:"); usersOne.forEach(user -> { System.out.printf("%s %s; ", user.getName(), user.getAge()); }); usersTwo.forEach(user -> { System.out.printf("%s %s; ", user.getName(), user.getAge()); });
另一个有用的方法是过滤器filter() ,让我们显示用户为小王的:
usersOne.stream().filter((p) -> ("小王".equals(p.getName()))) .forEach((user) -> System.out.printf("%s %s; ", user.getName(), user.getAge()));
我们也可以定义过滤器,然后重用它们来执行其他操作:
// 自定义过滤器 年龄大于18的Predicate<User> ageFilter = (p) -> (p.getAge() > 18);
usersOne.stream().filter(ageFilter).forEach((user) -> System.out.printf("%s %s; ", user.getName(), user.getAge()));
使用limit方法,可以限制结果集的个数:
System.out.println("最前面的3个用户:"); usersOne.stream().limit(3) .forEach((user) -> System.out.printf("%s %s; ", user.getName(), user.getAge()));System.out.println("最前面的3个年龄大于18的"); usersOne.stream() .filter(ageFilter) .limit(3) .forEach((user) -> System.out.printf("%s %s; ", user.getName(), user.getAge()));
排序呢? 我们在stream中能处理吗? 答案是肯定的。 在下面的例子中,我们将根据名字放到一个list中,然后显示列表:
System.out.println("排序"); List<User> collect = usersOne .stream() .sorted((u, u2) -> (u.getName().compareTo(u2.getName()))) .limit(3) .collect(toList());
collect.forEach(user -> {System.out.printf("%s %s; ", user.getName(), user.getAge());});
如果我们只对年龄大小感兴趣,比排序后选择第一个/最后一个 更快的是min和max方法:
System.out.println("年龄最小的:"); User user = usersOne .stream() .min((u1, u2) -> (u1.getAge() - u2.getAge())) .get();
System.out.printf(" %s %s;", user.getName(), user.getAge());
System.out.println("年龄最大的:"); User user1 = usersOne .stream() .max((u1, u2) -> (u1.getAge() - u2.getAge())) .get();
System.out.printf(" %s %s;", user1.getName(), user1.getAge());
上面的例子中我们已经看到 collect 方法是如何工作的。 结合 map 方法,我们可以使用 collect 方法来将我们的结果集放到一个字符串,一个 Set 或一个TreeSet中:
System.out.println("将 usersOne 的name 拼接成字符串:"); String us = usersOne .stream() .map(User::getName) .collect(joining("; "));
System.out.println(us);
System.out.println("将 usersOne 的name 存放到 Set:"); Set<String> usSet = usersOne .stream() .map(User::getName) .collect(toSet());
System.out.println(usSet);
System.out.println("将 usersOne 的name 存放到 TreeSet:"); TreeSet<String> usTreeSet = usersOne .stream() .map(User::getName) .collect(toCollection(TreeSet::new));
System.out.println(usTreeSet);
Streams 还可以是并行的(parallel)。 示例如下:
System.out.println("计算所有用户的所有年龄和:"); int totalAge = usersOne .parallelStream() .mapToInt(p -> p.getAge()) .sum();
System.out.println(totalAge);
我们可以使用summaryStatistics方法获得stream 中元素的各种汇总数据。 接下来,我们可以访问这些方法,比如getMax, getMin, getSum或getAverage:
//计算 count, min, max, sum, and average for numbers List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); IntSummaryStatistics stats = numbers .stream() .mapToInt((x) -> x) .summaryStatistics();
System.out.println("List中最大的数字 : " + stats.getMax()); System.out.println("List中最小的数字 : " + stats.getMin()); System.out.println("所有数字的总和 : " + stats.getSum()); System.out.println("所有数字的平均值 : " + stats.getAverage());
// 使用匿名内部类 new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }).start();
// 使用 lambda expression new Thread(() -> System.out.println("Hello world !")).start();
// 使用匿名内部类 Runnable race1 = new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }; // 使用 lambda expression Runnable race2 = () -> System.out.println("Hello world !"); // 直接调用 run 方法 race1.run(); race2.run();
ok 目前能想到的只有这么多 希望喜欢