前言
Lambda表达式于Java1.8更新引入 极大的简化了对函数式编程的复杂度
目录
一、Lambda表达式
Lambda表达式允许Java将函数作为一个参数传入 让你从此告别匿名内部类
举个简单的例子:按字符串长度来排序输出
List<String> list = Arrays.asList("java","python","scala","c++");
//普通匿名内部类方式 重写比较器 又臭又长
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
System.out.println(list.toString());
//lambda表达式 两行搞定
Collections.sort(list,(a,b)->a.length()-b.length());
list.forEach(System.out::println);
Lambda表达式就是如此简单 心动了吗?
1.Lambda表达式的适用场景
Lambda表达式适用于任何有函数式接口的地方 函数式接口即为只有一个抽象方法的接口(Object类中的方法除外)
2.Lambda表达式的特点
1.函数式编程 继面向接口编程之后另一大思想:面向函数编程
2.参数类型自动推断 传进去就可以 完全“散养”
3.代码量少 简洁 看起来很牛逼(笑)
3.基本格式
Lambda表达式根据不同的情况有很多很多种写法 如下所示
()->{};
()->{System.out.println(1);};
()-> System.out.println(1);
()->{return 100};
()->100;
()->null;
(x)->x+1;
x->x+1;
(int x)->{return x+1};
如果后续只有一个语句 {}可以省略不写 ()也可以省略不写 但是推荐不要吝啬那么两个括号
4.一些官方定义的函数式接口
Supplier | 代表一个输出 |
Consumer | 代表一个输入 |
BiConsumer | 代表两个输入 |
Function | 代表一个输入一个输出(一般类型不同 也可相同) |
UnaryOperator | 代表一个输入一个输出(类型相同) |
BiFunction | 代表两个输入一个输出(一般类型不同也可以相同) |
BiUnaryOperator | 代表两个输入一个输出(类型相同) |
5.官方函数式接口用法与自定义函数式接口
5.1Supplier的用法
Supplier<String> supplier = ()-> "aaa";
System.out.println(supplier.get());
5.2Consumer的用法
Consumer<String> consumer = (String string)-> System.out.println(string);
consumer.accept("bbb");
5.3BiConsumer的用法
BiConsumer<String,Integer> biConsumer = (str,num)-> System.out.println(str+"\n"+num);
biConsumer.accept("abc",4);
5.4Function的用法
Function<String,Integer> function = (String str)-> {return str.length();};
System.out.println(function.apply("abcdefg"));
5.5UnaryOperator的用法
UnaryOperator<String> unaryOperator = (str)-> {return str;};
System.out.println(unaryOperator.apply("lwpaizw"));
5.6BiFunction的用法
BiFunction<String,String,Integer> biFunction = (str,str1)->{return str.length()+str1.length();};
System.out.println(biFunction.apply("bbb","ccc"));
5.7BiUnaryOperator的用法
BinaryOperator<String> binaryOperator = (str,str1)->{return str+str1;};
System.out.println(binaryOperator.apply("alwb", "aizw"));
5.8自定义函数式接口
//第一个函数式接口
@FunctionalInterface
public interface StudentDao {
void get(Student student);
}
//第二个函数式接口
@FunctionalInterface
public interface TeacherDao {
int insert(Teacher teacher);
}
//一些操作 Student Teacher类里面什么都没有就不放出来了
TeacherDao teacherDao = (teacher)->{return 1;};
TeacherDao teacherDao1 = (Teacher teacher)->{return 2;};
TeacherDao teacherDao2 = (teacher)->3;
System.out.println(teacherDao.insert(new Teacher()));
System.out.println(teacherDao1.insert(new Teacher()));
System.out.println(teacherDao2.insert(new Teacher()));
StudentDao studentDao = (student)-> System.out.println("zhangsan");
StudentDao studentDao1 = (student)-> System.out.println("插入学生"+student); studentDao1.get(new Student());
studentDao.get(new Student());
二、方法引用
1.什么是方法引用
方法引用是用来直接访问类或实例已经存在的方法或构造方法 提供了一种引用而不执行方法的方式
2.方法引用的分类与规范格式
2.1规范格式
类型 | 语法 | 对应的Lambda表达式 |
静态方法引用 | 类名::staticMethod | (args)->类名.staticMethod(args) |
实例方法引用 | inst::instMethod | (args)->inst.instMethod(args) |
对象方法引用 | 类名::instMethod | (args)->类名.instMethod(args) |
构造方法引用 | 类名::new | (args)->new 类名(args) |
2.2静态方法引用
//如果函数式接口的实现可以通过调用另一个静态方法实现 就可以适用静态方法引用
public class Test1 {
public static void main(String[] args) {
Supplier<String> s1 = ()->Test1.put();
Supplier<String> s2 = Test1::put;
System.out.println(s1.get());
System.out.println(s2.get());
Consumer<String> c1 = (str)->Test1.put();
Consumer<Integer> c2 = Test1::fun;
c1.accept("abc");
c2.accept(123);
Function<String,String> f1 = (str)->Test1.toUpperCase(str);
Function<String,String> f2 = Test1::toUpperCase;
Function<String,String> f3 = (str)->{return Test1.toUpperCase(str);};
Function<String,String> f4 = Play::too;
System.out.println(f1.apply("abc"));
System.out.println(f2.apply("abc"));
System.out.println(f3.apply("abc"));
System.out.println(f4.apply("abc"));
}
static String put(){
System.out.println("put");
return "put....";
}
static Integer fun(int i){
System.out.println(i);
return 1;
}
static String toUpperCase(String str){
return str.toUpperCase();
}
}
class Play{
static String too(String string){
return string.toUpperCase();
}
}
2.3实例方法引用
//如果函数式接口的实现可以通过调用另一个实例的方法实现 就可以使用实例方法引用
public class Test2 {
public static void main(String[] args) {
Supplier<String> s1 = ()->new Test2().fun();
Supplier<String> s2 = new Test2()::fun;
System.out.println(s1.get());
System.out.println(s2.get());
//每次都要new一个对象好麻烦
Consumer<Integer> c1 = (size)->new Test2().getSize(size);
Consumer<Integer> c2 = new Test2()::getSize;
Test2 test = new Test2(); //所以我们唯一的创建一个Test2对象 就不用一直new了
Consumer<Integer> c3 = (size)->test.getSize(size);
Consumer<Integer> c4 = test::getSize;
c1.accept(123);
c2.accept(123);
c3.accept(123);
c4.accept(123);
Function<String,String> f1 = (str)->test.toUpperCase(str);
Function<String,String> f2 = test::toUpperCase;
System.out.println(f1.apply("abc"));
System.out.println(f2.apply("abc"));
BiFunction<String,String,Integer> bf = (str,str2)->test.too(str,str2);
BiFunction<String,String,Integer> bf2 = test::too;
System.out.println(bf.apply("abc", "def"));
System.out.println(bf2.apply("abc", "def"));
}
public String fun(){
return "put...";
}
public void getSize(int size){
System.out.println("size:"+size);
}
public void foo(String string){
System.out.println("foo");
}
public String toUpperCase(String str){
return str.toUpperCase();
}
public Integer too(String str,String str2){
return str.length()+str2.length();
}
}
2.4对象方法引用
//其实我感觉 对象方法引用和实例方法引用区别不大...很少会用到
public class Test3 {
public static void main(String[] args) {
Consumer<Too> c1 = (too)->new Too().fun();
c1.accept(new Too());
Consumer<Too> c2 = (too)->new Too2().fun();
c2.accept(new Too());
Consumer<Too> c3 = Too::fun;
c3.accept(new Too());
Consumer<Too2> c4 = Too2::fun;
c4.accept(new Too2());
BiConsumer<Too2,String> bc1 = (too2,str)->new Too2().show(str);
BiConsumer<Too2,String> bc2 = Too2::show;
bc1.accept(new Too2(),"aaa");
bc2.accept(new Too2(),"bbb");
BiFunction<Too,String,Integer> bf1 = (too,str)->new Too().play(str);
BiFunction<Too,String,Integer> bf2 = Too::play;
bf1.apply(new Too(),"aaa");
bf2.apply(new Too(),"bbb");
BiFunction<Too,String,Integer> bf3 = (too,str)->new Too().foo(str);
bf3.apply(new Too(),"abc");
BiFunction<Too,String,Integer> bf4 = Too::foo;
bf4.apply(new Too(),"def");
}
}
class Too{
public void fun(){
System.out.println("funning....");
}
public Integer foo(String s){
System.out.println("foo...");
return 1;
}
public Integer play(String s){
System.out.println("playing...." +s);
return 1;
}
}
class Too2{
public void fun(){
System.out.println("funning....2");
}
public Integer foo(String s){
return 1;
}
public void show(String str){
System.out.println("show Too2");
}
}
2.5构造方法引用
//如果函数式接口的实现可以通过调用另一个类的构造方法来实现 就可以使用构造方法引用
public class Test4 {
public static void main(String[] args) {
Supplier<Person> s1 = ()->new Person();
s1.get();
Supplier<Person> s2 = Person::new;
s2.get();
Supplier<List> s3 = ArrayList::new;
Supplier<Set> s4 = HashSet::new;
Supplier<Thread> s5 = Thread::new;
s3.get();
s4.get();
s5.get();
Consumer<String> c1 = (name)->new Account(name);
Consumer<String> c2 = Account::new;
Consumer<Integer> c3 = (age)->new Account(age);
Consumer<Integer> c4 = Account::new;
c1.accept("aaa");
c2.accept("bbb");
c3.accept(1);
c4.accept(2);
Function<String,Account> f1 = (str)->new Account(str);
f1.apply("abc");
Function<Integer,Account> f2 = Account::new;
f2.apply(1);
}
}
class Person{
public Person(){
System.out.println("调用无参构造方法");
}
}
class Account{
public Account(String name){
System.out.println("调用name构造方法" + name);
}
public Account(int age){
System.out.println("调用age构造方法" + age);
}
}
三、StreamApi
Stream是一组是用来处理数组与集合的API
1.Stream的特性
1.不是数据结构 没有内存存储
2.不支持索引访问
3.延迟计算
4.支持并行
5.很容易生成数组或集合(我好喜欢)
6.支持过滤、查找、转换、汇总、聚合等操作
2.Stream运行机制
Stream分为源source 中间操作 终止操作
流的源source可以是一个数组 一个集合 一个IO流等等
Stream只有遇到终止操作 才会开始执行遍历操作
3.Stream的创建
1.通过数组
//通过数组创建stream
static void gen1(){
String[] str = {"a","b","c","d"};
Stream<String> strs = Stream.of(str);
strs.forEach(System.out::println);
}
2.通过集合
//通过集合创建stream
static void gen2(){
List<String> list = Arrays.asList("1","2","3","4");
Stream<String> list1 = list.stream();
list1.forEach(System.out::println);
}
3.通过Stream.generate方法
/通过generate方法创建stream
static void gen3(){
Stream<Integer> generate = Stream.generate(() -> 1);
generate.limit(10).forEach(System.out::println);
}
4.通过Stream.inerate方法
//通过iterator方法创建stream
static void gen4(){
Stream<Integer> iterate = Stream.iterate(1, x -> x + 1);
iterate.limit(10).forEach(System.out::println);
}
5.通过其他API
//通过其他方式创建stream
static void gen5(){
String str = "abcdefg";
IntStream chars = str.chars();
chars.forEach(System.out::println);
}
4.Stream常用API
中间操作有
过滤 | filter |
去重 | distinct |
排序 | sorted |
截取 | limit、skip |
转换 | map、flatMap |
其他 | peek |
终止操作有
循环 | forEach |
计算 | min、max、count、average |
匹配 | anyMatch、allMatch、noneMatch、findAny、findFirst |
汇聚 | reduce |
收集器 | toArray collect |
5.具体用法
//找出全部的偶数 使用filter方法过滤
Arrays.asList(1,2,3,4,5).stream().filter((x)->x%2==0).forEach(System.out::println);
//求出结果集中所有偶数的总数 使用count方法统计
long count = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().filter((x) -> x % 2 == 0).count();
System.out.println(count);
//求出结果集中所有偶数的和 使用IntStream中的sum方法计算
int sum = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().filter((x) -> x % 2 == 0).mapToInt(x->x).sum();
System.out.println(sum);
//求集合中的最大值 使用max方法
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6);
Optional<Integer> max = integers.stream().max((a, b) -> a - b);
System.out.println(max);
System.out.println(max.get());
//求集合中的最小值 使用min方法 与最大值类似
System.out.println(integers.stream().min((a, b) -> a - b).get());
//求集合中符合条件的一个值 使用findany方法 findfirst方法与之类似
Optional<Integer> any = integers.stream().filter((x) -> x % 2 == 0).findAny();
System.out.println(any.get());
//求集合中最大值与最小值 不使用max与min方法 使用sorted方法
Optional<Integer> any1 = integers.stream().sorted().findAny();
System.out.println(any1.get());
Optional<Integer> any2 = integers.stream().sorted((a, b) -> b - a).findAny();
System.out.println(any2.get());
//排序字符串 使用sorted方法
//按字典顺序排序
Arrays.asList("java","c#","python","scala").stream().sorted().forEach(System.out::println);
//按字符串长度排序
Arrays.asList("java","c#","python","scala").stream().sorted((a,b)->a.length()-b.length()).forEach(System.out::println);
//将集合中元素进行过滤 同时返回一个集合对象 使用collect方法
List<Integer> collect = integers.stream().filter((x) -> x % 2 == 0).collect(Collectors.toList());
collect.forEach(System.out::println);
//去重 使用distince方法与使用collect方法
List<Integer> integers1 = Arrays.asList(1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 7);
Stream<Integer> distinct = integers1.stream().distinct();
distinct.forEach(System.out::println);
Arrays.asList(1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 7).stream().distinct().forEach(System.out::println);
Arrays.asList(1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 7).stream().collect(Collectors.toSet()).forEach(System.out::println);
//打印20-30的集合数据 使用iterate、limit、skip方法
Stream.iterate(1,(x)->x+1).limit(50).skip(20).limit(10).forEach(System.out::println);
//求字符串中数字的和 结合方法引用
String str = "11,22,33,44,55";
System.out.println(Stream.of(str.split(",")).mapToInt((x) -> Integer.valueOf(x)).sum());
System.out.println(Stream.of(str.split(",")).map((x) -> Integer.valueOf(x)).mapToInt((x) -> x).sum());
System.out.println(Stream.of(str.split(",")).mapToInt(Integer::valueOf).sum());
System.out.println(Stream.of(str.split(",")).map(Integer::valueOf).mapToInt((x) -> x).sum());
//创建自定义对象
String string = "java,c++,python";
Stream.of(string.split(",")).map((x)->new Person(x)).forEach(System.out::println);
Stream.of(string.split(",")).map(Person::new).forEach(System.out::println);
Stream.of(string.split(",")).map((x)->Person.build(x)).forEach(System.out::println);
Stream.of(string.split(",")).map(Person::build).forEach(System.out::println);
//求字符串中数字的和 并将每个数打印 使用peek方法
System.out.println(Stream.of(str.split(",")).peek(System.out::println).mapToInt(Integer::valueOf).sum());
//allMatch方法
System.out.println(Arrays.asList(1, 2, 3, 4).stream().allMatch(x -> x > 0));
//合并两个stream流 使用concat方法
List<String> strings2 = Arrays.asList("a", "l", "w", "b", "a", "i");
List<String> strings3 = Arrays.asList("z", "w");
Stream stream1 = strings2.stream();
Stream stream2 = strings3.stream();
Stream stream3 = Stream.concat(stream1,stream2);
stream3.forEach(System.out::println);
// stream3.forEach((s)-> System.out.println(s));
// System.out.println(stream3.collect(Collectors.toList()));
//flatmaap方法
List<Map<String,List<Student2>>> students = getStudent();
List<Student2> boy = students.stream().flatMap((classMap) -> classMap.get("男生").stream()).collect(Collectors.toList());
System.out.println(boy);
}
private static List<Map<String,List<Student2>>> getStudent(){
Map<String,List<Student2>> classMap = new HashMap<>();
List<Student2> boys = new ArrayList<>();
List<Student2> girls = new ArrayList<>();
boys.add(new Student2("zhangsan",14,"1-1","男"));
boys.add(new Student2("lisi",14,"1-2","男"));
boys.add(new Student2("wangwu",14,"1-3","男"));
girls.add(new Student2("xiaohong",14,"1-4","女"));
girls.add(new Student2("xiaomei",14,"1-5","女"));
girls.add(new Student2("xiaolan",14,"1-6","女"));
classMap.put("男生",boys);
classMap.put("女生",girls);
Map<String,List<Student2>> classMap2 = new HashMap<>();
List<Student2> boys1 = new ArrayList<>();
List<Student2> girls1 = new ArrayList<>();
boys1.add(new Student2("zhangsan",14,"2-1","男"));
boys1.add(new Student2("lisi",14,"2-2","男"));
boys1.add(new Student2("wangwu",14,"2-3","男"));
girls1.add(new Student2("xiaohong",14,"2-4","女"));
girls1.add(new Student2("xiaomei",14,"2-5","女"));
girls1.add(new Student2("xiaolan",14,"2-6","女"));
classMap2.put("男生",boys1);
classMap2.put("女生",girls1);
List<Map<String,List<Student2>>> gradeList = new ArrayList<>();
gradeList.add(classMap);
gradeList.add(classMap2);
return gradeList;
}
四、一段很有趣的代码
让我们看一看这段代码:
//一段有趣的代码
Stream<Integer> integerStream = integers.stream().filter((x) -> {
System.out.println("运行代码");
return x % 2 == 0;
});
System.out.println(integerStream.findAny().get());
integers的值是1,2,3,4,5,6
你们觉得会输出几个“运行代码”呢?
答案是两个
不妨想想是为什么 试着修改一下integers的值再看看输出结果呢?
总结
以上就是Lambda表达式、方法引用、Stream的用法了 之所以学习Lambda表达式 一方面减少自己的工作量 一方面也要看懂这是什么 别人为什么这样写 还有就是 别人用匿名内部类写了很长很长 而我使用Lambda表达式写了一两行 我们实现了同样的需求 这样不就显得很牛吗(哈哈哈)
还记得我们提到过面向函数编程这一思想 Java1.8之所以费这么大力气来引入函数式编程 主要原因是
1.代码简洁 意图明确 使用stream让你告别for循环
2.Java函数式编程使编写并运行程序变得如此简单 真的很简单!
假设有一个需求(是什么无所谓了) Java可能需要三十行代码 而python等语言可能四五行就搞定了 我觉得这可能也是引入函数式编程的一个原因吧