JDK8引入的Lambda表达式在简化程序代码方面大显身手,在程序遍历访问集合中元素的场合,运用Lambda表达式可以大大简化操纵集合的程序代码。他本质只是一颗让编程人员更加得心用手的“语法糖”。下面引入实例介绍Lambda的应用场景:
1、Lambda表达式的基本用法(遍历访问List列表)
List<String> list = new ArrayList<>();
Collections.addAll(list,new String[]{"Tome","Make","Jike","Honey"});
//传统的List遍历集合
for(String name : list){
System.out.print(name+",");
}
//方式二:使用Lambda表达式
list.forEach((String name)->{ System.out.print(name+","); });
//方式三:在 Java 8 中使用双冒号操作符
list.forEach(System.out::print);
比较3种遍历集合的代码,不难发现,使用Lambda表达式可以简化程序代码,Lambda的基本语法为:
(Type1 param1,Type2 param2,...,TypeN paramN)->{
statment1;
statment2;
return statmentM
}
Lambda表达式在某些条件下可以再简化:
(1)、参数类型可以省略。在绝大多数情况下,编译器都可以从上下文中聪明的推断出Lambda表达式的参数类型,对于以上的Lambda表达式,编译器能推断出name变量的类型为String,因此Lambda表达式可以简化为:
list.forEach((name)->{ System.out.print(name+","); });
(2)、当Lambda表达式的参数个数只有一个时,可以省略小括号。以上Lambda表达式可以简化为:
list.forEach(name->{ System.out.print(name+","); });
(3)、当Lambda表达式只包含一条语句时,可以省略大括号、语句结尾的分号,此外,当return语句没有返回值时也可以省略。
list.forEach(name-> System.out.print(name+","));
(4)、Lambda表达式中符号“->”后面也可以包含一个普通的表达式,语法为:
(Type1 param1,Type2 param2,...,TypeN paramN)->(expression)
例如:( int a,int b) -> (a*b+2)
2、Lambda表达式中的方法引用
上述代码方法三中“::”符是因为在编译器能根据上下文来推断Lambda表达式的参数的场合,可以在Lambda表达式中省略参数,直接通过“::”符号来引用方法。方法引用的语法格式有以下3中:
第一种方式:objectName::instanceMethod //引用实例方法
第二种方式: ClassName::staticMethod //引用静态方法
第三种方式: ClassName::instanceMethod //引用实例方法
下面举例说明:
x-> System.out.println(x) 等同于: System.out::println; //引用实例方法
(x,y)->Math.max(x,y) 等同于: Math::max //引用静态方法
x->x.toLowerCase() 等同于: String::toLowerCase //引用实例方法
3、用Lambda表达式代替内部类
//一、使用匿名内部类
new Thread(new Runnable() {
public void run() {
System.out.println("Runnable Implement By Anonymous Inner Class");
}
}).start();
//二、使用Lambda表达式
new Thread(()->System.out.println("Runnable Implement By Lambda")).start();
//三、使用Lambda表达式
Runnable runnable = ()->{ System.out.println("Runnable Implement"); };
new Thread(runnable).start();
方式二和方式三的Lambda表达式相当于创建了实现Runnable接口的匿名对象;由于Runnable接口的run()方法不带参数,因此,Lambda表达式的参数列表也相应为空“()”,Lambda表达式中符号“->”后面的可执行语句块相当于run()方法的方法体。
4、用Lambda表达式对集合进行排序
List<String> list = new ArrayList<>();
Collections.addAll(list,new String[]{"Tome","Make","Jike","Honey"});
//方式一:通过创建匿名的Comparator实例来排序
Comparator<String> cp = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return (o1.compareTo(o2));
}
};
Collections.sort(list,cp);
//方式二:用Lymbda表达式来排序
Comparator<String> comparator =(String name1,String name2)-> (name1.compareTo(name2));
Collections.sort(list,comparator);
//方式三:用Lymbda表达式来排序
Collections.sort(list,(String name1,String name2)->(name1.compareTo(name2)));
list.forEach(System.out::println);
以上方式二和方式三的Lambda表达式相当于创建了Comparator类型的匿名对象,由于Comparator接口的
compare(T name1,T name2)方法有两个参数,所以Lymbda表达式也有两个相应的参数(String name1,String name2),Lambda 表达式中符号“->” 后面的表达式“name1.compareTo(name2)”相当于compare(T name1,T name2)方法的方法体。
5、Lambda表达式与Steam联合使用
从JDK8开始,专门抽象出了java.util.stream.Stream流接口,他可以充当Java集合的适配器,使得程序能够按照操纵流的方式来访问集合中的元素
Stream是对集合的包装,通常和lambda一起使用。 使用lambdas可以支持许多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。 同样,Stream使用懒运算,他们并不会真正地读取所有数据,遇到像getFirst() 这样的方法就会结束链式语法。 在接下来的例子中,我们将探索lambdas和streams 能做什么。 我们创建了一个Person类并使用这个类来添加一些数据到list中,将用于进一步流操作。 Person 只是一个简单的POJO类:
Person.java:
package lambda;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
创建一个包含Person对象的ArrayList列表,接着调用他的stream()方法得到一个流,接着再对流中的元素进行过滤和排序等操作。
LambdaForCollectionAction.java:
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class LambdaForCollectionAction {
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
Collections.addAll(persons,
new Person("Jack",20),
new Person("Jack",25),
new Person("Mike",30),
new Person("Mary",19),
new Person("Sun",29));
persons.stream()
.filter(p ->p.getAge()>20) //过滤条件为年龄大于20
.forEach(p -> System.out.println(p.getName()+":"+p.getAge()));
persons.stream()
.sorted((p1,p2)->(p1.getAge()-p2.getAge()))//按照年龄排序
.limit(3) //取出3个元素
.forEach(p -> System.out.println(p.getName()+":"+p.getAge()));
int maxAge = persons.parallelStream()//获得并行流
//把包含Person对象的流映射为保存其age属性的int类型流
.mapToInt(p->p.getAge())
.max()
.getAsInt();
System.out.println("Max Age :"+maxAge);
}
}
在ColTester类的main()方法中,persons.stream()方法返回一个Stream对象,接下来调用Stream对象的filter(),sort()和forEach()方法时,传入的都是Lymbda表达式,Stream接口的filter()方法的完整声明为:
/**
* Returns a stream consisting of the elements of this stream that match
* the given predicate.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* predicate to apply to each element to determine if it
* should be included
* @return the new stream
*/
Stream<T> filter(Predicate<? super T> predicate);
以上filter()方法有一个Predicate类型的参数,用来指明过滤数据的条件。Predicate接口的 boolean test(T t);方法判断参数t是否符合过滤条件,如果符合就返回true,否则就返回false。以上程序代码“filter(p ->p.getAge()>20)” 通过Lambda表达式创建了一个Predicate匿名对象,把他传给filter()方法。Lymbda表达式中"->"后面的表达式"p.getAge()>20"相当于是test(T t)方法的方法体。
Collection接口的parallelStream()方法返回一个采用并行处理的Stream对象,当集合中有大量数据时,为了提高处理集合中元素的效率,可以调用此方法来得到Stream对象,它的内部实现会开启多个线程来并发处理数据。