文章目录
一、lambda表达式
二、方法引用
三、Stream流
一、lambda表达式
在Java中,Lambda表达式是一种函数式编程的特性,它允许以简洁的语法编写函数或操作,而无需显式创建匿名类。Lambda表达式在Java 8中引入,是使Java更适应现代编程风格和需求的重要工具。lambda表达式可以代替简单的匿名内部类的实现。
lambda表达式的一个重要作用就是能够简写代码。
需要注意的是,lambda表达式只适用于函数式接口,也就是接口内只定义了一个方法的接口。
如果一个接口是函数式接口一般会使用 @FunctionalInterface 来表示。
例如:forEach遍历中的接口就是一个函数式接口。
lambda表达式语法
(parameters) -> { statements; }
1
parameters:这是Lambda表达式的参数列表。参数类型是可选的,也可以直接推断。例如,a -> a + 1 和 (int a) -> a + 1 是等效的。
->:这是Lambda操作符,将参数列表和Lambda体分开。
expression:这是Lambda体的单个表达式。如果Lambda体包含多个语句,则它们必须被大括号 {} 包围。
需要了解的是在实现 lambd表达式时可以按规则来简写lambda表达式:
在小括号()内,如果只有一个形参,或者形象类型只有一种,那么形参类型可以省略
在大括号内{},如果只有一条语句,那么 大括号可以省略、return可以省略、分号也可以省略。
以下通过具体案例来说明这个简写规则。
首先先定义一个set集合,我们都知道set集合是无索引的,那么set集合常规的遍历方式只有迭代器遍历与forEach遍历。
Set<String> s=new HashSet<>();
// set集合:无序、不重复、无索引
s.add("张三");
s.add("李四");
s.add("王五");
1
2
3
4
5
1、迭代器遍历
// 迭代器方式遍历set集合
// 创建迭代器
Iterator<String> it = s.iterator();
// 判断当前迭代器指针是否指向最后一个位置
while(it.hasNext()){
// 返回迭代器指针当前指向的元素,并且移动迭代器指针
String str = it.next();
System.out.println(str);
}
1
2
3
4
5
6
7
8
9
2、forEach遍历
// foreach循环遍历set集合
for(String i : s){
System.out.println(i);
}
1
2
3
4
以上两种遍历方式都能实现遍历set集合的要求,但是代码还是不够简洁,以下我们来通过从匿名内部类到lambda表达式的改进。
匿名内部类
//完整版
s.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
1
2
3
4
5
6
7
lambda表达式
s.forEach((String i) -> {
System.out.println(i);
});
1
2
3
根据简化规则的lambda表达式
s.forEach((o1) -> System.out.println(o1) ); //极简版
1
以上效果都是一样的,但是可以看到lambda表达式的代码结构清晰了许多,这就是lambda表达式的作用。
二、方法引用
在Java中,方法引用是一种简化Lambda表达式的方式,它允许你直接引用现有的方法,而不是重新定义一个Lambda表达式。
方法引用在Java 8中引入,它是一种非常方便的语法糖,可以使代码更加简洁、易读。
常用的方法引用分为引用静态方法、引用成员方法等。
1、引用静态方法
语法:类名 :: 静态方法名,这里出现的新操作符 :: 这是英文状态的冒号组成的。
需要注意的是这个语法中方法名是没有圆括号的。
例如:
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(System.out::println);
1
2
在上面的例子中,System.out::println 是一个方法引用。它引用了 System.out 类的静态方法 println。这个方法接受一个参数(在这个例子中是 String 类型),并打印这个参数。
引用静态方法规则:
方法名和参数:在引用静态方法时,你需要提供方法的名称以及所需要的任何参数。例如,如果你有一个名为greet的静态方法,它需要一个字符串和一个整数作为参数,你可以这样引用它:ClassName.greet(参数1, 参数2)。
作用域:你可以在任何地方引用静态方法,包括在类的内部,其他类的内部,或者作为表达式的一部分。
访问权限:如果静态方法是私有的,那么你不能从类的外部访问它。如果它是受保护的,那么你可以从同一个包中的类或者其他包中的子类访问它。如果它是默认的(即没有修饰符),那么你可以从同一个包中的类访问它。
重载:你可以在一个类中拥有多个同名的静态方法,只要他们的参数列表不同(即形成了重载)。在引用时,Java会根据你提供的参数类型和数量来确定应该调用哪个方法。
泛型:虽然静态方法可以使用泛型,但是他们在引用时并不需要提供具体的类型参数。例如,如果你有一个名为calculate的静态方法,它需要一个List作为参数,你可以这样调用它:ClassName.calculate(myList),而不必提供类型参数。
访问静态方法的方式:你可以通过类名来访问静态方法,或者通过实例来访问(虽然这有些混淆,因为通常我们通过实例来调用实例方法,而不是静态方法)。例如,ClassName.methodName()或者instanceName.methodName()。
链式调用:如果一个静态方法的返回值是返回类型的实例,那么我们可以连续地调用这个实例的方法。例如,如果add方法返回一个Calculator实例,并且这个实例有一个calculate方法,那么我们可以这样使用:ClassName.add(1, 2).calculate()。
2、引用成员方法
引用对象的方法是指通过对象来引用其成员方法。
方法引用的语法格式为:object::methodName,其中object为对象实例,methodName为对象所属类中的成员方法名。
例如:
class Animal {
String name;
void makeSound() {
System.out.println("The animal makes a sound");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.name = "Cat";
// 引用对象方法
MethodReferenceDemo demo = new MethodReferenceDemo();
demo.printAnimalInfo(animal::makeSound, animal);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这个示例中,我们定义了一个Animal类,并在Main类中创建了一个Animal对象。我们使用animal::makeSound方法引用了Animal对象的makeSound方法。在printAnimalInfo方法中,我们使用Lambda表达式来处理被引用的方法,同时将Animal对象作为参数传递给该方法。
请注意,这里的MethodReferenceDemo和printAnimalInfo方法需要根据你的实际需求进行相应的实现。
需要注意的是,方法引用的目标必须是只有一个没有参数的方法或多个具有相同参数的方法。如果目标只有一个已命名的参数,则无法使用方法引用,只能使用Lambda表达式。
三、Stream流
Java中的Stream流是一种新的特性,它提供了一种处理集合和数组的方式,可以极大地方便我们对集合、数组等数据源进行连续操作。Stream流可以让我们以一种更加简洁、高效、可读性更强的方式来处理数据。
Stream流的操作可以分为中间操作和终止操作两种类型。中间操作返回的是一个新的Stream流,而终止操作返回的是一个非Stream类型的结果。
在Stream流的处理过程中,数据是惰性求值的,只有在执行终止操作时才会开始处理数据。这种处理方式可以减少计算量和开销,提高效率。
创建Stream流可以从很多种数据源中创建,例如List、Set或者任何其他实现了Iterable接口的类。创建方式很简单,使用stream()或parallelStream()方法即可。
总的来说,Stream流是一种方便、简洁且高效的数据处理方式,它可以使代码更加简洁易懂,提高代码的可读性和可维护性。
常用的中间操作方法
/*
filter:为过滤方法,方法中形参为lambda表达式或实现类
stream流只能使用一次,建议使用链式编程
修改流中的数据,不会改变原数组或集合的值,类似于拷贝了一份进行操作而已
limit:获取stream流中前几个元素,形参为个数
skip:跳过stream流中形参个元素。
distinct:去重方法,当stream对象为自定义方法时,那么需要重写hashcode与equals方法
concat:合并两个stream流
map:转换流中数据类型
*/
1
2
3
4
5
6
7
8
9
10
常用的终止方法
/*
总结方法:
froEach方法,遍历
count:统计
toArray:将流中数据转换为指定类型数组进行返回。
collect:将流中数据收集进一个集合进行返回
*/
1
2
3
4
5
6
7
需要注意的是,在进行流处理的时候,中间方法与终止方法的区别在于中间方法会返回一个新的stream流,可以需要调用,而终止方法返回的是一个非stream流的结果。所以stream一般适合链式调用。
示例1: 使用stream流将List集合中的数据添加到map集合中,姓名做键,年龄做值。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class 方法 {
public static void main(String[] args) {
/*
总结方法:
froEach方法,遍历
count:统计
toArray:将流中数据转换为指定类型数组进行返回。
collect:将流中数据收集进一个集合进行返回
*/
List<String> list=new ArrayList<>();
Collections.addAll(list,"张无忌-男-18","wuhu-男-20","呀呼-女-20");
// 匿名内部类实现stream流
Map<String, String> user = list.stream().collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[0];
}
}
, new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[2];
}
}
)
);
// lambda实现stream流
Map<String, String> user2 = list.stream().collect(Collectors.toMap(o1 -> o1.split("-")[0], o2 -> o2.split("-")[2]));
user.forEach((o1,o2) -> System.out.println(o1+","+o2));
System.out.println(user2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
运行结果:
以上方法都是使用到了流处理的中间方法与终结方法。
具体查阅Java1.8Api文档。