Lambda表达式
首先什么是lambda表达式?
lambda表达式其实就是函数式接口,形式类似于(s)->{doSomething;}
其次什么是函数式接口?
函数式接口就是在接口中只存在一个方法的接口,例子如下:
package com.bdcloud.dao;
@FunctionalInterface
public interface MyFunctionInterface {
void myMethod();
}
结合上述函数式接口,使用lambda的例子如下:
package com.bdcloud.dao.impl;
import com.bdcloud.dao.MyFunctionInterface;
public class DemoMyFunctionInterface {
private static void doSomething(MyFunctionInterface myFunctionInterface){
myFunctionInterface.myMethod(); //调用自定义的函数接口方式
}
public static void main(String[] args) {
//传统方式写法
doSomething(new MyFunctionInterface() {
@Override
public void myMethod() {
System.out.println("此方法为匿名函数类");
}
});
//lambda的写法
doSomething(()->{
System.out.println("此调用方式为lambda方式");
});
}
}
Lambda的延迟特性
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。
Demo:例如方法调用中参数为字符串拼接结果,故而无论最终是否需要,都需要先拼接再进行函数调用,例子如下:
public class Demo01Logger {
private static void log(int level, String msg) {
if (level == 1) {
System.out.println(msg);
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(1, msgA + msgB + msgC);
}
}
这样,在方法调用传参过程中,则可以使用函数式编程来优化性能,具体实现如下:
@FunctionalInterface
public interface MessageBuilder {
String buildMessage();
}
public class Demo02LoggerLambda {
private static void log(int level, MessageBuilder builder) {
if (level == 1) {
System.out.println(builder.buildMessage());
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(1, () ‐> msgA + msgB + msgC );
}
}
这样一来,只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。
Lambda作为方法的参数或者返回值
Demo如下:
package com.bdcloud.thread;
/***
* Runable 的run方法,Lambda表达式作为参数
*/
public class DemoRunable {
private static void startThread(Runnable runnable){
new Thread(runnable).start();
}
public static void main(String[] args) {
startThread(()->{
System.out.println("开启一个线程");
});
}
}
Lambda作为返回值
package com.bdcloud.thread;
import java.util.Arrays;
import java.util.Comparator;
/***
* 测试lambda表达式作为方法返回值
*/
public class LambdaReturn {
private static Comparator<String> newCompare(){
return (a,b) -> b.length() - a.length();
}
public static void main(String[] args) {
String [] arrs = {"aaa","bbbb","ccccc"};
System.out.println(Arrays.toString(arrs));
Arrays.sort(arrs,newCompare());
System.out.println(Arrays.toString(arrs));
}
}
JDK中常用的三种函数式接口
第一种 Supplier接口,位于java.util.function包中,该接口只有一个 T get()方法,它的作用是生产一个对象,Demo如下
package com.bdcloud.supplier;
import java.util.function.Supplier;
/***
* 测试函数式接口-Supplier
* 生产接口,用于根据泛型生产相对应对象
*/
public class SupplierDemo {
private static String supplierTest(Supplier<String> sup){
return sup.get();
}
public static void main(String[] args) {
String test = supplierTest(() -> {
return "哇哈哈哈";
});
System.out.println(test);
}
}
第二种是Consumer接口,位于java.util.function包中,该接口只有一个accept(T t)方法,用于 消费对象,举个例子,编写一个Consumer的接口消费字符串,并且将字符串拆分成姓名: XXX 性别:X的形式
package com.bdcloud.consumer;
import java.util.function.Consumer;
/***
* 测试jdk函数式接口Consumer
*/
public class ConsumerTest {
/***
* andThen方法用于顺序调用俩个consumer接口
* @param consumer1
* @param consumer2
* @param str
*/
private static void fromatString(Consumer<String> consumer1,Consumer<String> consumer2,String str){
consumer1.andThen(consumer2).accept(str);
}
public static void main(String[] args) {
String [] strs= {"照例音,女","刘德华,男"};
for (String temp:
strs) {
fromatString((t)->{
System.out.print("姓名:"+t.split(",")[0]);
},(t)->{
System.out.println(" 性别:"+t.split(",")[1]);
},temp);
}
}
}
第三种 Predicate接口,位于java.util.function包中,boolean test(T t)的方法用于构造一个返回boolean的方法,举2个例子,第一个判断字符串中是否包含’H’ or|and ‘o’,Demo如下:
package com.bdcloud.predicate;
import java.util.function.Predicate;
/***
* 编程式接口-Predicate,返回一个boolean值
*/
public class PredicateDemo {
/***
* 测试test方法
* @param predicate
* @param value
* @return
*/
public static boolean method(Predicate<String> predicate,String value){
return predicate.test(value);
}
/***
* 测试and方法
* @param one
* @param two
*/
public static void testAnd(Predicate<String> one,Predicate<String> two){
boolean helloWorld = one.and(two).test("HelloWorld");
System.out.println("是否包含相关字符串"+helloWorld);
}
/***
* 测试Or方法
* @param one
* @param two
*/
public static void testOr(Predicate<String> one,Predicate<String>two){
boolean helloWorld = one.or(two).test("helloWorld");
System.out.println("测试字符串包含"+helloWorld);
}
/***
* 测试
* @param one
*/
public static void testNegate(Predicate<String>one){
System.out.println("测试亦或操作="+one.negate().test("HHHHHHHH"));
}
public static void main(String[] args) {
boolean method = method((value) -> {
return value.length() > 5;
}, "AAAAAAA");
System.out.println(method);
//测试And方法
testAnd((s)->{return s.contains("H");},(s)->{return s.contains("o");});
//测试Or方法
testOr((s)->{return s.contains("H");},(s)->{return s.contains("o");});
testNegate((s)->{return s.length()>5;});
}
}
第二个筛选字符串数组中中名字长度为3且为女性的字符串;Demo如下
package com.bdcloud.predicate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/***
* 做一个字符串筛选过滤
*/
public class PredicateDemo2 {
private static void filter(Predicate<String> one,Predicate<String>two,String[] arr){
List<String> result = new ArrayList<>();
for (String temp:
arr) {
if (one.and(two).test(temp)) {
result.add(temp);
}
}
System.out.println(result.toString());
}
public static void main(String[] args) {
String[] arrs = {"古力娜扎,女","黄晓明,男","丰绅殷德,女"};
filter((s)->{return s.split(",")[0].length() == 4;},(s)->{return s.split(",")[1].equals("女");},arrs);
}
}
最后一个Function接口,位于java.util.function包中,内部方法 R apply(T t),用于接收一个类型的对象,转换为另一种类型的对象,举个例子,将一个字符串转换成Int并且自增1并返回,代码如下:
package com.bdcloud.function;
import java.util.function.Function;
/***
* 测试函数式编程-Function接口
*/
public class FunctionDemo {
/***
* 测试将String转换为int
* @param function
*/
private static void changeNum(Function<String,Integer> function){
Integer apply = function.apply("100");
System.out.println("转换后="+apply);
}
/***
* 测试andThen
* @param function
* @param two
*/
private static void changeNumAndAdd(Function<String,Integer>function,Function<Integer,Integer>two){
Integer apply = function.andThen(two).apply("100");
System.out.println("结果="+apply);
}
public static void main(String[] args) {
changeNum((s)->{return Integer.valueOf(s);});
changeNumAndAdd((s)->{return Integer.valueOf(s);},(s)->{return s++;});
}
}
Stream流
随着jdk8函数式编程的引入,引入stream流的概念用于解决传统集合的弊端,过滤都是重型加载匹配,且不方便代码修改变更
Stream的一些特性
- 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
- 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
- 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
类似于Stream的filter操作可以使用lambda传递真实处理逻辑
package com.bdcloud.stream;
import org.omg.PortableInterceptor.INACTIVE;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
/***
* 使用Stream-API进行测试
*/
public class StreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("lisi");
list.add("yuanqianqian");
list.add("zhangfeng");
list.stream().filter(name -> name.startsWith("l")).filter(name-> name.length()==11).forEach(name-> System.out.println(name));
//map方法调用,将字符串转换为int
Stream<String> stringStream = Stream.of("1", "2", "3", "4");
stringStream.map((s->Integer.valueOf(s))).forEach((s-> System.out.println(s)));
//count函数
Stream<String> stringStream2 = Stream.of("1", "2", "3", "4");
System.out.println(stringStream2.count());
//只取用前n个。limit
Stream<String> stringStream3 = Stream.of("1", "2", "3", "4");
stringStream3.limit(3).forEach(s-> System.out.println(s));
//skip。跳过前几个
Stream<String> stringStream4 = Stream.of("1", "2", "3", "4");
stringStream4.skip(1).forEach(s-> System.out.println(s));
//组合 concat
Stream<String> stream1 = Stream.of("张三丰");
Stream<String> stream2 = Stream.of("张无忌");
Stream<String> concat = Stream.concat(stream1, stream2);
System.out.println(concat.count());
}
}
使用Stream的综合例子
package com.bdcloud.stream;
import com.bdcloud.bean.Person;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* 综合测试Stream方法
* 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
* 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
* 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
* 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
* 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
* 6. 根据姓名创建 Person 对象;存储到一个新集合中。
* 7. 打印整个队伍的Person对象信息。
*/
public class DemoArrayListNames {
public static void main(String[] args) {
//第一支队伍
ArrayList<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("石破天");
one.add("石中玉");
one.add("老子");
one.add("庄子");
one.add("洪七公");
//第二支队伍
ArrayList<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("赵丽颖");
two.add("张三丰");
two.add("尼古拉斯赵四");
two.add("张天爱");
two.add("张二狗");
Stream<String> stream1 = one.stream().filter(s -> s.length() == 3).limit(3);
Stream<String> stream2 = two.stream().filter(s -> s.startsWith("张")).skip(2);
Stream<String> concat = Stream.concat(stream1, stream2);
concat.map(s-> new Person(s)).forEach(s-> System.out.println(s));
}
}
方法引用
方法引用出现的初衷就是为了简化lambda表达式,lambda表达式传入的是一个解决方案,若这种解决方案在其他地方已经定义,那是否需要重复定义,所以就出现了方法引用,引用方式 对象::方法名
@FunctionalInterface
public interface Printable {
void print(String str);
}
在 Printable 接口当中唯一的抽象方法 print 接收一个字符串参数,目的就是为了打印显示它。那么通过Lambda来使用它的代码很简单:
public class Demo01PrintSimple {
private static void printString(Printable data) {
data.print("Hello, World!");
}
public static void main(String[] args) {
printString(s ‐> System.out.println(s));
}
}
若使用方法引用简化后,书写格式是这样的
public class Demo02PrintRef {
private static void printString(Printable data) {
data.print("Hello, World!");
}
public static void main(String[] args) {
printString(System.out::println);
}
}
通过对象名引用成员方法
若一个类中实现了对一个字符串转变大写并输出,类定义如下:
public class MethodRefObject {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
函数式接口定义如下:
@FunctionalInterface
public interface Printable {
void print(String str);
}
若此时想使用MethodRefObject中的printUpperCase作为该函数式接口的lambda实现,则使用对象名引用,使用代码如下:
public class Demo04MethodRef {
private static void printString(Printable lambda) {
lambda.print("Hello");
}
public static void main(String[] args) {
MethodRefObject obj = new MethodRefObject();
printString(obj::printUpperCase);
}
}
通过类名引用静态成员方法
@FunctionalInterface
public interface Calcable {
int calc(int num);
}
第一种使用lambda实现
public class Demo05Lambda {
private static void method(int num, Calcable lambda) {
System.out.println(lambda.calc(num));
}
public static void main(String[] args) {
method(‐10, n ‐> Math.abs(n));
}
}
第二种使用Math类的abs方法引用实现
public class Demo06Lambda {
private static void method(int num, Calcable lambda) {
System.out.println(lambda.calc(num));
}
public static void main(String[] args) {
method(‐10, Math::abs);
}
}
通过super/this引用成员方法
定义函数式接口如下:
@FunctionalInterface
public interface Greetable {
void greet();
}
父类Human的定义如下:
public class Human {
public void sayHello() {
System.out.println("Hello!");
}
}
子类的lambda调用与实现
public class Man extends Human {
@Override
public void sayHello() {
System.out.println("大家好,我是Man!");
}
//定义方法method,参数传递Greetable接口
public void method(Greetable g){
g.greet();
}
public void show(){
//调用method方法,使用Lambda表达式
method(()‐>{
//创建Human对象,调用sayHello方法
new Human().sayHello();
});
//简化Lambda
method(()‐>new Human().sayHello());
//使用super关键字代替父类对象
method(()‐>super.sayHello());
}
}
若使用super的对象引用如下:
method(super::sayHello);
通过类的构造引用
函数式接口定义如下:
public interface PersonBuilder {
Person buildPerson(String name);
}
类的实现如下:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
普通lambda的实现与调用
public class Demo09Lambda {
public static void printName(String name, PersonBuilder builder) {
System.out.println(builder.buildPerson(name).getName());
}
public static void main(String[] args) {
printName("赵丽颖", name ‐> new Person(name));
}
}
使用方法引用改造lambda
printName("赵丽颖", Person::new);