Java8中,最重要且最实用的莫过于Lambda表达式和Stream流式编程了。它改变了以往传统的编程方式,更高效、更简洁、并且代码更具有可读性。现在基本所有的大型互联网公司很早就摒弃JDK.1.7而使用JDK1.8了,但一些传统公司依然是使用1.7版本,它们并不愿意改变。原因有二:学习成本、维护成本。但是一旦你接触它之后,你就会爱上它,因为它实在是太棒了!所以我还是强烈推荐学习并使用Java8。我本人初次学习Java8时也是在传统公司,反反复复学了好几遍(很笨),每次都有不同的感悟,后来跳槽后,发现公司已经很早就已经全面使用Java8,故而我又学习了一遍Java8。只学不用很快就会忘了,最重要的还是在工作当中多多实践吧。
- 小李子①:筛选年龄大于18岁的童鞋
你可能会这样写:
public static List<Student> filterStudent(List<Student> list){
List<Student> result = new ArrayList<>();
for(Student stu : list){
if(18 < stu.getAge()){//筛选年龄大于18
result.add(stu);
}
}
return result;
}
但是接下来,我想筛选年龄大于20岁的呢?你可能会复制粘贴上边的代码,然后将逻辑修改一下
if( 20 < stu.getAge() ) result.add(stu);
又或者是筛选男女同学、班级、教师等等...很明显这样的做法是不够灵活的。那如果使用Java8中的Lambda实现呢?
list.stream().filter(stu -> stu.getAge() > 18)
.filter(stu -> stu.getSex() == 0)
.filter(stu -> stu.getClasses().equals("三年一班"))
//等同于
list.stream().filter(stu -> stu.getAge()>18 && stu.getSex()==0 && stu.getClasses().equals("三年一班"))
上面这段代码,它足够灵活,你可以随意的筛选元素。并且它可读性更好,看见这段代码就能猜到他是干嘛的:筛选年龄大于18岁,性别是男,并且在三年一班的学生。
注:list是学生列表;stream()是java8中的方法,用于将集合变成流。下文会介绍。
- 小李子②:Java中的传统线程代码
Thread t = new Thread(new Runnable(){
@override
public void run(){
System.out.println("Hello Lambda!");
}
});
在Lambda表达式中它的样子,它非常的简洁
Thread t = new Thread(() -> System.out.println("Hello Lambda!"));
- 小李子③:
Java 8 中常用的函数式接口
函数式接口 | 描述符 | 原始类型特化 | 使用场景 |
---|---|---|---|
Predicate<T> | T->boolean | IntPredicate,LongPredicate, DoublePredicate | 涉及到类型T的布尔表达式时使用此函数式接口 |
Consumer<T> | T->void | IntConsumer,LongConsumer, DoubleConsumer | 定义了一个accept的抽象方法,用于接收T对象,并且没有返回值。 如果你需要访问T对象,并且对其【执行某些操作】,即可使用此函数式接口。 |
Function<T,R> | T->R | IntFunction<R>, IntToDoubleFunction, IntToLongFunction LongFunction<R>, LongToDoubleFunction, LongToIntFunction, ToIntFunction<T>, ToDoubleFunction<T>, ToLongFunction<T> | 接收T对象,返回R对象 |
Supplier<T> | ()->T | BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier | 返回T对象(常用于创建对象) |
UnaryOperator<T> | T->T | IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator | 输出一个与入参一样的值 |
BinaryOperator<T> | (T,T)->T | IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator | 同类型运算 |
BiPredicate<L,R> | (L,R)->boolean | ||
BiConsumer<T,U> | (T,U)->void | ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T> | |
BiFunction<T,U,R> | (T,U)->R | ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U> | 接收T,U类型参数,返回R类型对象 |
Lambda实践
准备工作
public class LambdaDemo {
private static List<Apple> appleList;
/**
*静态代码块:初始化静态变量appleList
**/
static {
appleList = Arrays.asList(
new Apple("red", 500),
new Apple("blue", 20),
new Apple("red", 30),
new Apple("yellow", 20));
}
public static void main(String[] args){}
}
public class Apple {
private String color;
private int height;
}
public class LambdaUtils {
/**
* 筛选集合
* @param list 集合
* @param predicate boolean条件
* @param <T>
* @return
*/
public static <T> List<T> filter(List<T> list, Predicate<T> predicate){
List<T> result = new ArrayList<>();
for(T t : list){
if(predicate.test(t)){
result.add(t);
}
}
return result;
}
/**
* 输出集合元素
* @param str
* @param list
* @param <T>
*/
public static<T> void out(String str, List<T> list){
System.out.println(str + " ========START========");
for(T t : list){
System.out.println(t);
}
System.out.println(str + " =========END=========");
}
/**
* 改变集合中元素
* @param list
* @param consumer
* @param <T>
*/
public static <T> List<T> consumer(List<T> list, Consumer<T> consumer){
for(T t : list){
consumer.accept(t);
}
return list;
}
/**
* Function<T, R>:由 T 对象转 R 对象
* @param list
* @param function
* @param <T>
* @param <R>
* @return
*/
public static <T, R> List<R> function(List<T> list, Function<T, R> function){
List<R> result = new ArrayList<>();
for(T t : list){
result.add(function.apply(t));
}
return result;
}
1、筛选红苹果
LambdaUtils.filter(appleList, apple -> "red".equals(apple.getColor()))
2、筛选红苹果且重量大于50的苹果
LambdaUtils.filter(appleList,
apple -> "red".equals(apple.getColor()) && apple.getHeight() > 50)
3、改变苹果重量
LambdaUtils.consumer(appleList, apple -> apple.setHeight(50))
4、获取苹果重量的集合
//int -> Integer 装箱操作
List<Integer> integerFunction = LambdaUtils.function(appleList,
apple -> apple.getHeight());
注:装箱操作在性能上是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在JVM堆中。因此,装箱后的值需要更多的内存进行存储。
如何解决? 避免装箱就可以了,并且Function已经为我们提供了基本类型的函数式接口
//举个栗子
//装箱
Predicate<Integer> p = i -> i>0;
//避免装箱
IntPredicate intP = i -> i>0;
//p.test(-1);
//intP.test(-1);
方法引用
1、根据苹果的颜色进行排序
appleList.sort((Apple o1, Apple o2) -> o1.getColor().compareToIgnoreCase(o2.getColor()));
//Java编译器可以根据Lambda出现的上下文来推断Lambda表达式参数的类型
appleList.sort((o1, o2) -> o1.getColor().compareToIgnoreCase(o2.getColor()));
使用方法引用
//使用方法引用
List<String> collect = appleList.stream()
.map(apple -> apple.getColor()).collect(Collectors.toList());
collect.sort(String::compareToIgnoreCase);
2、另一种实现方式:Comparator
//Lambda
appleList.sort(Comparator.comparing(apple -> apple.getColor()));
//方法引用
appleList.sort(Comparator.comparing(Apple::getColor));
3、构造函数引用
Supplier<Apple> appleNew = () -> new Apple("yellow", 250);//创建一个apple对象
Apple apple = appleNew.get();
//使用方法引用与Lambda比较
Supplier<Apple> appleSupplier = Apple::new;
BiFunction<String, Integer, Apple> biFunction = Apple::new;
BiFunction<String, Integer, Apple> biLambda = (color, height) -> new Apple(color, height);
Apple green = biFunction.apply("green", 666);
Apple green_lambda = biLambda.apply("green", 777);
复合Lambda表达式
1、比较器复合
苹果重量排序,逆序排序
appleList.sort(Comparator.comparing(Apple::getHeight).reversed());
按照重量进行排序,当两个苹果重量一样时,按照颜色进行排序
appleList.sort(Comparator.comparing(Apple::getHeight).thenComparing(Apple::getColor));
2、谓词复合
negate:非
and:和
or:或
Apple apple = new Apple("yellow", 100);
Predicate<Apple> predicate = a -> a.getHeight()>120;//苹果重量大于120的函数式表达式
predicate.test(apple);//false
predicate.negate().test(apple);//true
//在重量小于120的同时,再加上绿色的筛选条件
predicate.negate().and(a -> "yellow".equals(a.getColor())).test(apple);//true
predicate.or(a -> "yellow".equals(a.getColor())).test(apple);//true
3、函数复合
andThen():X.andThen(Y),先执行x,再执行y
compose():X.compose(Y),先执行Y,再执行X
//先加1,再乘以2
Function<Integer, Integer> add = x -> x + 1;
Function<Integer, Integer> multiply = x -> x * 2;
Function<Integer, Integer> andThen = add.andThen(multiply);
Integer result = andThen.apply(2);
System.out.println("【andThen】 x + 1 => x * 2 = " + result);
//先 * 2 再 + 1
Function<Integer, Integer> compose = add.compose(multiply);
System.out.println("【compose】 x * 2 => x + 1 = " + compose.apply(2));
//总结:andThen [先] 执行调用者。 compose [后] 执行调用者
参考:《Java 8 实战》