Java8 Lambda表达式
前言:
编程的模式有面向对象,面向过程,面向函数。
对函数编程语言的支持目前有Scala、Erlang、F#、Python、Php、Java、JavaScript等好处:
使代码更加紧凑
java7写法:new Thread(new Runnable() { @Override public void run() { System.out.println("java7写法"+" 当前线程是 "+Thread.currentThread().getName()); } }).start();
java8 Lambda表达式写法:
new Thread(() ->System.out.println("java8 lambda表达式 "+" 当前线程是 "+Thread.currentThread().getName())).start();
Console 输出结果如下:
java7写法 当前线程是 Thread-0 java8 lambda表达式 当前线程是 Thread-1
方法的参数可以是一个函数接口单元。
java7写法:
public static void main(String[] args) { String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; Arrays.sort(s,new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(), o2.length()); } }); }
java8 Lambda表达式写法:
public static void main(String[] args) { String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; /** * 利用Arrays(工具类).sort(String[] a, Comparator<? super String> c) * * 将String类型数组中的元素按长度进行排序。 * */ Arrays.sort(s, (param1,param2) ->Integer.compare(param1.length(), param2.length()));; /** * 利用StreamAPI#forEach()来输出排序后数组元素。 */ Stream.of(s).forEach(parameter ->System.out.println(parameter)); }
由上面可知,使用Lambda表达式可以改变一个函数接口的行为,而不需要去实现Comparable接口。
Syntax(句法):
( Parameters参数1 ,参数2,... ) ->{ expressions语句块1; 语句块2 ; (return)...;}
注意点:
- 当只有一个参数时,大括号可以省略
- 当一个语句块,花括号可以省略
- 当有返回值,且多个语句块时,返回值的语句块需添加return.
这和JavaScript中ES6语法中箭头函数类似的,都是面向方法的编程,在前言也提到了。
方法的引用:
若是需执行的代码在某些类中已经存在,则不需要去写Lambda表达式,可以直接引用该方法。这叫做方法引用。
未引用前的代码:
Stream.of(datas).forEach(param -> {System.out.println(param);});
使用方法引用了的代码:
/** * 对一个类中已经存在的方法的引用。方法引用,这里是class::staticMethod(类::静态方法) */ Stream.of(s).forEach(System.out::println);
方法引用的具体分类:
Object:instanceMethod Class:staticMethod Class:instanceMethod
解释:
第一种和第二种差不多,都是将参数传递给方法。System.out::println 等同于 x -> System.out.println(x)
第三种是第一参数是方法执行的目标
/** * 对一个类已经存在的方法的引用。方法引用,这里是class::instanceMethod(类::对象调用的方法) * String s1,s2; * s.compareTo(s2); */ Arrays.sort(s, String:: compareTo);
String::compareToIgnoreCase 等同于 (x,y) -> x.compareToIgnoreCase(y)
构造方法引用:
构造方法引用与方法引用不同,构造方法引用的方法是new.
例子:String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; Stream<String> stream=Stream.of(s); String[] s1=stream.toArray(String[] :: new);
构建方法引用有以下两种形式:
Class::new Class[]::new
Lambda表达式作用域:
Lanmbda表达式的变量作用域与内部类类似,但是放宽了内部类引用外部类的变量的条件。
在java7中内部类引用外部类的变量,案例如下:
final String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; new Thread(new Runnable() { @Override public void run() { System.out.println(s[0]); } }).start();
在java8Lambda表达式中,写法如下:
String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; new Thread(() -> System.out.println([0])).start();
Console控制台中输出结果如下:
可发现,java8中放款了对引用条件,不再强制要求加上final关键字。
但是,java8要求这个变量是effectively final。Effectively final:有效的只读变量,一旦定义后,在后面中不能随意修改的。
注意点:Java中内部类以及Lambda表达式中也不允许修改外部类中的变量,这是为了避免多线程情况下的race condition
Lambda表达式可用的变量:
Lambda表达式可以访问传递给它的变量,可以访问它自己内部定义的变量,也可以访问外部的变量。
注意点: Lambda表达式访问的外部变量有一个限制,变量的引用地址不可变。这和上面提到effectively final的变量是同一个道理。
Lambda表达式中this:
在Lambda表达式中,this不是指向的Lambda所产生的那个对象,而是声明它的外部对象。
public class Write { public static void main(String[] args) { new Write().lambdaTest(); } public void lambdaTest(){ String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; Arrays.asList(s) .stream() .map((str)->{ System.out.println(" this指向对象的名字: "+this.getName()); return str.toUpperCase(); }) .collect(Collectors.toList()) .forEach(System.out::println); } public String getName(){ return Write.class.getSimpleName(); } }
Console控制台输出信息:
this指向对象的名字: Write this指向对象的名字: Write this指向对象的名字: Write 红太狼MOTHER 灰太狼FATHER 小灰灰SON
Stream API特性
理解(引用来源):
1.Stream是元素的集合,这点让Stream看起来用些类似Iterator;
2.可以支持顺序和并行的对原Stream进行汇聚的操作;大家可以把Stream当成一个装饰后的Iterator。原始版本的Iterator,用户只能逐个遍历元素并对其执行某些操作;包装后的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,具体这些操作如何应用到每个元素上,就给Stream就好了!原先是人告诉计算机一步一步怎么做,现在是告诉计算机做什么,计算机自己决定怎么做。当然这个“怎么做”还是比较弱的
Stream使用介绍:
基本使用步骤:
- 创建Stream对象;
- 转换Stream对象,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换);
- 对Stream进行聚合(Reduce)操作,最终获取想要的结果;
创建Stream对象:
Stream中静态方法:
Stream.of()方法创建//参数是一个对象(数组或者类对象) Stream.of(new String[]{"新根"});
Stream.generate()方法创建:
//参数是一个Supplier接口对象,该接口用于创建给定类型的对象。 Stream.generate(() -> Math.random());
Stream.iterate(seed, f)方法创建:
Stream.iterate("新", (params)->{ return params+"根";}).limit(1);
注意点:需使用limit()限制迭代次数,不然会无限循环下去。
通过Collection接口的默认方法:
各种Collection接口,或者其子类都具备stream()
String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; Arrays.asList(s) .stream();
Stream转换:
distinct():去掉重复的元素,形成新的Stream.
filter():对元素进行过滤,新的Stream只包含过滤后的元素
map():对元素进行遍历转换,新的Stream只包含新转换的元素
flatMap:对元素进行遍历,新的Stream包含新转换的元素和原本的元素
peek():
limit():获取前几个元素,形成新的Stream
skip():跳过第几个元素开始形成新的Stream.
Stream Reduce(汇聚):
含义:汇聚操作(也称为折叠)接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果。
可变汇聚:
2.1 collect方法是将Stream中要用到的元素全部收集到一个结果容器。其他汇聚:
3.1 reduce方法: 参考:博客教程2
3.2 count方法:获取Stream中元素的个数
代码分析:
String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"}; Arrays.asList(s) .stream() .map((str)->{ System.out.println(" this指向对象的名字: "+this.getName()); return str.toUpperCase(); }) .collect(Collectors.toList()) .forEach(System.out::println);
从上面的代码可知:
- stream()创建了一个Stream对象。
- map()进行一个Stream对象的转换
- collect()进行reduce Stream
- 最后循环输出结果
资源参考: