Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)
经典例子:
@Test
public void demo () {
Runnable runnable = new Runnable(){
public void run(){
System.out.println("Test thread");
}
};
new Thread(runnable).start();
}
/*
Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
为了指定run的方法体,不得不需要Runnable接口的实现类;
为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
而实际上,似乎只有方法体才是关键所在
*/
转换为Lambda的写法是:
@Test
public void lambdaDemo () {
new Thread(() -> System.out.println("Test lambda")).start();
}
/*
上例中,核心代码其实只是如下所示的内容:() -> System.out.println(“Test lambda")
*/
Lambda格式:
(参数类型 参数名称) -> { 代码语句 }
格式说明:
- 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
- ->是新引入的语法格式,代表指向动作。
- 大括号内的语法与传统方法体要求基本一致
实用案例:
public interface Cooker {
void makeFood();
}
public class demo01 {
public static void main(String[] args) {
invokeCook(() -> {
System.out.println("吃饭啦!");
});
}
private static void invokeCook(Cooker c){
c.makeFood();
}
}
排序:
// 老式的写法
public class Demo02 {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("chock", 19),
new Person("聪", 18),
new Person("儿", 20) };
// 匿名内部类
Comparator<Person> comp = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge() - o1.getAge();
}
};
Arrays.sort(array, comp); // 第二个参数为排序规则,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
}
}
}
// lambda写法
public class demo02 {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("chock", 19),
new Person("聪", 18),
new Person("儿", 20) };
Arrays.sort(array,(Person p1,Person p2) -> {
return p2.getAge() - p1.getAge();
});
for (Person person : array) {
System.out.println(person);
}
}
}
遍历:
@Test
public void eachLambda () {
List<String> strList = Arrays.asList("chock", "聪", "儿");
strList.forEach(str -> System.out.println(str));
}
// 使用lambda的遍历,使用foreach方法,其中代码中的str表示遍历中的每个元素,代码块部分可以对该元素进行不同的操作
Predicate的使用:
jdk8中提供了Predicate的条件表达式方法,可以简便地进行多条件操作
其中Predicate之间提供了and(),or() 和 negate()方法来对多条件之间进行 与 或 取反 操作
@Test
public void predicateLambda () {
List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
Predicate<String> unStartJ = a -> !a.startsWith("J");
Predicate<String> longerFourLetter = a -> a.length() > 4;
languages.stream().filter(unStartJ.and(longerFourLetter)).forEach(str -> System.out.println(str));
}
Map 和 Reduce操作:
其中Map操作是可以对流中的数据进行再处理
@Test
public void mapLambda () {
List<Integer> beforeVals = Arrays.asList(100, 200, 300, 400, 500);
beforeVals.stream().map(n -> n / 100) .forEach(System.out::println);
}
// 输出的结果为: 12345
Reduce操作是在流中得到特定的值,这个特定的值是经过指定的计算后得出,以下例子中是计算所有购买商品打8折后的总价
首先使用map算出所有单件商品的8折价格是多少,然后使用reduce方法进行求和
这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素。
@Test
public void reduceLambda() {
double bill = Stream.of(100, 200, 300, 400, 500).map((cost) -> .8 * cost)
.reduce((sum, cost) -> sum + cost)
.get();
System.out.println(bill); // 1200.0
}
变形:网上购买后需要添加10块钱的邮费,所以reduce的参数中多了一个identity的初始值(10)
第二个变形,与第一种变形相同的是都会接受一个BinaryOperator函数接口,不同的是其会接受一个identity参数,用来指定Stream循环的初始值。如果Stream为空,就直接返回该值。
@Test
public void reduceLambda() {
double bill = Stream.of(100, 200, 300, 400, 500).map((cost) -> .8 * cost)
.reduce(10.0, (sum, cost) -> {
sum += cost;
return sum;
});
System.out.println(bill); // 1210.0
}
Collect操作:
collect可以对流处理后的结果进行归总
@Test
public void lastOperationLambda () {
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany",
"Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase())
.collect(Collectors.joining(", "));
System.out.println(G7Countries); // Collectors.join() 把List<String> 转换为拼接字符串
G7.stream().map(x -> x.toUpperCase())
.collect(Collectors.toList()).forEach(System.out::println); // Collectors.toList() 把处理结果重新封装为list
}
IntSummaryStatistics:
主要配合Stream进行使用,可以对数字结果进行常规的函数操作,包括最大最小值,平均值等……
@Test
public void intSummaryStatisticsLambda () {
List<Integer> vals = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics summary = vals.stream().mapToInt(n -> n + 2).summaryStatistics();
System.out.println("最大值:" + summary.getMax() + " 平均值:" + summary.getAverage());
}
distinct和skip:
distinct主要是对列表中的数据进行去重,skip是在后续操作中跳过多少个元素
@Test
public void distinctAndSkipLambda() {
List languages = Arrays.asList("Java", "Scala", "C++", "Java", "Haskell", "Lisp");
languages.stream().distinct().skip(2).forEach(n -> System.out.println(n));
}
list TO map 和 分组:
@Test
public void listToMapAndGroupByLambda () {
List<Person> list = new ArrayList<Person>() {{
add(new Person(1, "name1"));
add(new Person(2, "name2"));
add(new Person(3, "name3"));
}};
// list转换为id为key,对象为val的MAP
Map<Integer, Person> map = list.stream().collect(Collectors.toMap(Person::getId, n -> n));
// list按照id进行分组,在id存在重复的情况下可以拆分
Map<Integer, List<Person>> map2 = list.stream().collect(Collectors.groupingBy(Person::getId));
}
Collectors 类的静态工厂方法汇总:
方法名 | 返回类型 | 说明 |
---|---|---|
toList | List | 把流中所有项目收集到一个 List |
toSet | Set | 把流中所有项目收集到一个 Set,删除重复项 |
toCollection | Collection | 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new) |
counting | Long | 计算流中元素的个数 |
sumInt | Integer | 对流中项目的一个整数属性求和 |
averagingInt | Double | 计算流中项目 Integer 属性的平均值 |
summarizingInt | IntSummaryStatistics | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 |
joining | String | 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", ")) |
maxBy | Optional | 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() |
minBy | Optional | 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() |
reducing | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size)) |
groupingBy | Map<K, List> | 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 |
partitioningBy | Map<Boolean,List> | 根据对流中每个项目应用谓词的结果来对项目进行分区 |