Java Stream API
Stream API
Java8提供了一个全新的API - Stream。引入这个Stream的主要目的,一个是可以支持更好的并发;一个是通过使用Stream可以向方法传递代码(更简洁的形式)
Collections.sort(list, new Comparator<Stu>() {
@Override
public int compare(Stu o1, Stu o2) {
return o1.score - o2.score;
}
});
之前如果我们想给函数传递代码,需要通过匿名类的形式,可以看见这种形式比较繁琐。使用Java8的写法如下
Collections.sort(list, (o1, o2) -> o1.score - o2.score);
是不是马上就简单了很多呢!
行为参数化传递代码
** 更好地传递代码 **
class Stu {
private int age;
private int score;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
public static List<Stu> filterScoreBigThan60(List<Stu> list) {
List<Stu> ret = new ArrayList<>();
for (Stu stu : list) {
if (stu.getScore() > 60) {
ret.add(stu);
}
}
return ret;
}
public static List<Stu> filterAgeMoreThan15(List<Stu> list) {
List<Stu> ret = new ArrayList<>();
for (Stu stu : list) {
if (stu.getAge() > 15) {
ret.add(stu);
}
}
return ret;
}
我们自定义了一个Stu类,下面写了两个静态方法,用来过滤Stu的。观察这两段静态方法的代码,你发现什么了吗?
没错,我们会发现,这两段代码高度地相同。如果我们现在要再写一个过滤超过80分地学生,难道要在把代码复制粘贴一边?这肯定不是很好的实现方式,因为相同代码过多,我们一旦粘贴复制,很有可能会出现忘改某一些地方的情况 如果你现实中做过开发,搞过这种类似需求的代码,你一定明白我说的是啥。复制代码,但是某一些关键要改的地方不小心给漏掉了。而且每一个条件都要新建一个函数,会非常繁琐,函数名也会变得越来越长。
ok,问题我们是知道了,怎么解决了?
- 使用策略模式解决
filterStuByInterface(list, new MyPredicate() {
@Override
public boolean test(Stu stu) {
return stu.getAge() > 60;
}
});
public static List<Stu> filterStuByInterface(List<Stu> list, MyPredicate predicate) {
List<Stu> ret = new ArrayList<>();
for (Stu stu : list) {
if (predicate.test(stu)) {
ret.add(stu);
}
}
return ret;
}
我们把一组策略封装到一个策略里面,这里面使用的一个接口。所有的策略都实现这个接口。这样我们的方法就是通用的,每次使用的时候只要传入对应的策略就可以了。
- 使用Lambda表达式解决
public static List<Stu> filterSte(List<Stu> list, Predicate<Stu> predicate) {
List<Stu> ret = new ArrayList<>();
for (Stu stu : list) {
if (predicate.test(stu)) {
ret.add(stu);
}
}
return ret;
}
Predicate predicate这个是什么意思呢?Predicate是一个泛型接口,里面有一个test用来返回boolean值。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
我们随便通过使用策略模式也可以解决问题,但是通过对比Lambda表达式,我们发现Lambda表达式表达地更加简洁易懂。
Lambda表达式
Lambda 表达式是 Java 8 引入的一项重要特性,它使得代码更加简洁和易读,尤其是在处理集合、并发编程等方面。Lambda 表达式允许你以更函数式的方式编写代码,而不需要显式地创建匿名内部类。
Lambda 表达式的语法
Lambda 表达式的语法非常简洁,通常包括三个部分:参数列表、箭头符号(->)、方法体。
(parameters) -> expression
或
(parameters) -> { statements; }
方法引用
方法引用是一种简化 Lambda 表达式的方式,当 Lambda 表达式只是调用一个已有的方法时,可以使用方法引用来代替 Lambda 表达式。方法引用有以下几种形式:
- 静态方法引用:ClassName::staticMethod
- 实例方法引用:instance::instanceMethod
- 特定类型的方法引用:Type::method
- 构造方法引用:ClassName::new
Lambda 表达式的实际应用
集合操作
Java 8 引入了流(Stream API),它可以与 Lambda 表达式一起使用,从而简化集合的操作,如过滤、映射、排序等。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 过滤出长度大于 5 的名字,并转换为大写
List<String> filteredNames = names.stream()
.filter(name -> name.length() > 5)
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(filteredNames); // 输出 [CHARLIE]
并发编程
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("Task completed by " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.shutdown();
Lambda 表达式的注意事项
- 访问外部变量:Lambda 表达式可以访问外部的局部变量,但这些变量必须是有效的最终变量(effectively final),即它们在初始化后不能被修改。
- 多线程安全:虽然 Lambda 表达式本身是线程安全的,但如果它操作共享资源,则需要考虑同步问题。
- 性能:Lambda 表达式通常比匿名内部类更高效,因为它们避免了额外的类加载和内存分配。
总结
Lambda 表达式是 Java 8 引入的一个强大特性,它使代码更加简洁、易读,并且促进了函数式编程风格的应用。通过 Lambda 表达式,你可以更方便地处理集合、并发编程等问题。希望这些介绍和示例能帮助你更好地理解和使用 Lambda 表达式。如果有任何具体问题或需要进一步的帮助,请随时提问!