Java中Lambda表达式的使用
本文内容纲要:
简介
(译者注:虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能。本人建议不要乱用,因为这就和某些很高级的黑客写的代码一样,简洁,难懂,难以调试,维护人员想骂娘.)
Lambda表达式是JavaSE8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。
Lambda表达式还增强了集合库。JavaSE8添加了2个对集合数据进行批量操作的包:java.util.function包以及java.util.stream包。流(stream)就如同迭代器(iterator),但附加了许多额外的功能。总的来说,lambda表达式和stream是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。在本文中,我们将从简单到复杂的示例中见认识lambda表达式和stream的强悍。
环境准备
如果还没有安装Java8,那么你应该先安装才能使用lambda和stream(译者建议在虚拟机中安装,测试使用)。像NetBeans和IntelliJIDEA一类的工具和IDE就支持Java8特性,包括lambda表达式,可重复的注解,紧凑的概要文件和其他特性。
下面是JavaSE8和NetBeansIDE8的下载链接:
JavaPlatform(JDK8):从Oracle下载Java8,也可以和NetBeansIDE一起下载
NetBeansIDE8:从NetBeans官网下载NetBeansIDE
Lambda表达式的语法
基本语法:
(parameters)->expression
或
(parameters)->{statements;}
下面是Javalambda表达式的简单例子:
//1.不需要参数,返回值为5
()->5
//2.接收一个参数(数字类型),返回其2倍的值
x->2*x
//3.接受2个参数(数字),并返回他们的差值
(x,y)->x–y
//4.接收2个int型整数,返回他们的和
(intx,inty)->x+y
//5.接受一个string对象,并在控制台打印,不返回任何值(看起来像是返回void)
(Strings)->System.out.print(s)
基本的Lambda例子
现在,我们已经知道什么是lambda表达式,让我们先从一些基本的例子开始。在本节中,我们将看到lambda表达式如何影响我们编码的方式。假设有一个玩家List,程序员可以使用for语句("for循环")来遍历,在JavaSE8中可以转换为另一种形式:
String[]atp={"RafaelNadal","NovakDjokovic",
"StanislasWawrinka",
"DavidFerrer","RogerFederer",
"AndyMurray","TomasBerdych",
"JuanMartinDelPotro"};
List<String>players=Arrays.asList(atp);
//以前的循环方式
for(Stringplayer:players){
System.out.print(player+";");
}
//使用lambda表达式以及函数操作(functionaloperation)
players.forEach((player)->System.out.print(player+";"));
//在Java8中使用双冒号操作符(doublecolonoperator)
players.forEach(System.out::println);
正如您看到的,lambda表达式可以将我们的代码缩减到一行。另一个例子是在图形用户界面程序中,匿名类可以使用lambda表达式来代替。同样,在实现Runnable接口时也可以这样使用:
//使用匿名内部类
btn.setOnAction(newEventHandler<ActionEvent>(){
@Override
publicvoidhandle(ActionEventevent){
System.out.println("HelloWorld!");
}
});
//或者使用lambdaexpression
btn.setOnAction(event->System.out.println("HelloWorld!"));
下面是使用lambdas来实现Runnable接口的示例:
//1.1使用匿名内部类
newThread(newRunnable(){
@Override
publicvoidrun(){
System.out.println("Helloworld!");
}
}).start();
//1.2使用lambdaexpression
newThread(()->System.out.println("Helloworld!")).start();
//2.1使用匿名内部类
Runnablerace1=newRunnable(){
@Override
publicvoidrun(){
System.out.println("Helloworld!");
}
};
//2.2使用lambdaexpression
Runnablerace2=()->System.out.println("Helloworld!");
//直接调用run方法(没开新线程哦!)
race1.run();
race2.run();
Runnable的lambda表达式,使用块格式,将五行代码转换成单行语句。接下来,在下一节中我们将使用lambdas对集合进行排序。
使用Lambdas排序集合
在Java中,Comparator类被用来排序集合。在下面的例子中,我们将根据球员的name,surname,name长度以及最后一个字母。和前面的示例一样,先使用匿名内部类来排序,然后再使用lambda表达式精简我们的代码。
在第一个例子中,我们将根据name来排序list。使用旧的方式,代码如下所示:
String[]players={"RafaelNadal","NovakDjokovic",
"StanislasWawrinka","DavidFerrer",
"RogerFederer","AndyMurray",
"TomasBerdych","JuanMartinDelPotro",
"RichardGasquet","JohnIsner"};
//1.1使用匿名内部类根据name排序players
Arrays.sort(players,newComparator<String>(){
@Override
publicintcompare(Strings1,Strings2){
return(s1.compareTo(s2));
}
});
使用lambdas,可以通过下面的代码实现同样的功能:
//1.2使用lambdaexpression排序players
Comparator<String>sortByName=(Strings1,Strings2)->(s1.compareTo(s2));
Arrays.sort(players,sortByName);
//1.3也可以采用如下形式:
Arrays.sort(players,(Strings1,Strings2)->(s1.compareTo(s2)));
其他的排序如下所示。和上面的示例一样,代码分别通过匿名内部类和一些lambda表达式来实现Comparator:
//1.1使用匿名内部类根据surname排序players
Arrays.sort(players,newComparator<String>(){
@Override
publicintcompare(Strings1,Strings2){
return(s1.substring(s1.indexOf("")).compareTo(s2.substring(s2.indexOf(""))));
}
});
//1.2使用lambdaexpression排序,根据surname
Comparator<String>sortBySurname=(Strings1,Strings2)->
(s1.substring(s1.indexOf("")).compareTo(s2.substring(s2.indexOf(""))));
Arrays.sort(players,sortBySurname);
//1.3或者这样,怀疑原作者是不是想错了,括号好多...
Arrays.sort(players,(Strings1,Strings2)->
(s1.substring(s1.indexOf("")).compareTo(s2.substring(s2.indexOf(""))))
);
//2.1使用匿名内部类根据namelenght排序players
Arrays.sort(players,newComparator<String>(){
@Override
publicintcompare(Strings1,Strings2){
return(s1.length()-s2.length());
}
});
//2.2使用lambdaexpression排序,根据namelenght
Comparator<String>sortByNameLenght=(Strings1,Strings2)->(s1.length()-s2.length());
Arrays.sort(players,sortByNameLenght);
//2.3orthis
Arrays.sort(players,(Strings1,Strings2)->(s1.length()-s2.length()));
//3.1使用匿名内部类排序players,根据最后一个字母
Arrays.sort(players,newComparator<String>(){
@Override
publicintcompare(Strings1,Strings2){
return(s1.charAt(s1.length()-1)-s2.charAt(s2.length()-1));
}
});
//3.2使用lambdaexpression排序,根据最后一个字母
Comparator<String>sortByLastLetter=
(Strings1,Strings2)->
(s1.charAt(s1.length()-1)-s2.charAt(s2.length()-1));
Arrays.sort(players,sortByLastLetter);
//3.3orthis
Arrays.sort(players,(Strings1,Strings2)->(s1.charAt(s1.length()-1)-s2.charAt(s2.length()-1)));
就是这样,简洁又直观。在下一节中我们将探索更多lambdas的能力,并将其与stream结合起来使用。
使用Lambdas和Streams
Stream是对集合的包装,通常和lambda一起使用。使用lambdas可以支持许多操作,如map,filter,limit,sorted,count,min,max,sum,collect等等。同样,Stream使用懒运算,他们并不会真正地读取所有数据,遇到像getFirst()这样的方法就会结束链式语法。在接下来的例子中,我们将探索lambdas和streams能做什么。我们创建了一个Person类并使用这个类来添加一些数据到list中,将用于进一步流操作。Person只是一个简单的POJO类:
publicclassPerson{
privateStringfirstName,lastName,job,gender;
privateintsalary,age;
publicPerson(StringfirstName,StringlastName,Stringjob,
Stringgender,intage,intsalary){
this.firstName=firstName;
this.lastName=lastName;
this.gender=gender;
this.age=age;
this.job=job;
this.salary=salary;
}
//GetterandSetter
//.....
}
接下来,我们将创建两个list,都用来存放Person对象:
List<Person>javaProgrammers=newArrayList<Person>(){
{
add(newPerson("Elsdon","Jaycob","Javaprogrammer","male",43,2000));
add(newPerson("Tamsen","Brittany","Javaprogrammer","female",23,1500));
add(newPerson("Floyd","Donny","Javaprogrammer","male",33,1800));
add(newPerson("Sindy","Jonie","Javaprogrammer","female",32,1600));
add(newPerson("Vere","Hervey","Javaprogrammer","male",22,1200));
add(newPerson("Maude","Jaimie","Javaprogrammer","female",27,1900));
add(newPerson("Shawn","Randall","Javaprogrammer","male",30,2300));
add(newPerson("Jayden","Corrina","Javaprogrammer","female",35,1700));
add(newPerson("Palmer","Dene","Javaprogrammer","male",33,2000));
add(newPerson("Addison","Pam","Javaprogrammer","female",34,1300));
}
};
List<Person>phpProgrammers=newArrayList<Person>(){
{
add(newPerson("Jarrod","Pace","PHPprogrammer","male",34,1550));
add(newPerson("Clarette","Cicely","PHPprogrammer","female",23,1200));
add(newPerson("Victor","Channing","PHPprogrammer","male",32,1600));
add(newPerson("Tori","Sheryl","PHPprogrammer","female",21,1000));
add(newPerson("Osborne","Shad","PHPprogrammer","male",32,1100));
add(newPerson("Rosalind","Layla","PHPprogrammer","female",25,1300));
add(newPerson("Fraser","Hewie","PHPprogrammer","male",36,1100));
add(newPerson("Quinn","Tamara","PHPprogrammer","female",21,1000));
add(newPerson("Alvin","Lance","PHPprogrammer","male",38,1600));
add(newPerson("Evonne","Shari","PHPprogrammer","female",40,1800));
}
};
现在我们使用forEach方法来迭代输出上述列表:
System.out.println("所有程序员的姓名:");
javaProgrammers.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
phpProgrammers.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
我们同样使用forEach方法,增加程序员的工资5%:
System.out.println("给程序员加薪5%:");
Consumer<Person>giveRaise=e->e.setSalary(e.getSalary()/100*5+e.getSalary());
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);
另一个有用的方法是过滤器filter(),让我们显示月薪超过1400美元的PHP程序员:
System.out.println("下面是月薪超过$1,400的PHP程序员:")
phpProgrammers.stream()
.filter((p)->(p.getSalary()>1400))
.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
我们也可以定义过滤器,然后重用它们来执行其他操作:
//定义filters
Predicate<Person>ageFilter=(p)->(p.getAge()>25);
Predicate<Person>salaryFilter=(p)->(p.getSalary()>1400);
Predicate<Person>genderFilter=(p)->("female".equals(p.getGender()));
System.out.println("下面是年龄大于24岁且月薪在$1,400以上的女PHP程序员:");
phpProgrammers.stream()
.filter(ageFilter)
.filter(salaryFilter)
.filter(genderFilter)
.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
//重用filters
System.out.println("年龄大于24岁的女性Javaprogrammers:");
javaProgrammers.stream()
.filter(ageFilter)
.filter(genderFilter)
.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
使用limit方法,可以限制结果集的个数:
System.out.println("最前面的3个Javaprogrammers:");
javaProgrammers.stream()
.limit(3)
.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
System.out.println("最前面的3个女性Javaprogrammers:");
javaProgrammers.stream()
.filter(genderFilter)
.limit(3)
.forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName()));
排序呢?我们在stream中能处理吗?答案是肯定的。在下面的例子中,我们将根据名字和薪水排序Java程序员,放到一个list中,然后显示列表:
System.out.println("根据name排序,并显示前5个Javaprogrammers:");
List<Person>sortedJavaProgrammers=javaProgrammers
.stream()
.sorted((p,p2)->(p.getFirstName().compareTo(p2.getFirstName())))
.limit(5)
.collect(toList());
sortedJavaProgrammers.forEach((p)->System.out.printf("%s%s;%n",p.getFirstName(),p.getLastName()));
System.out.println("根据salary排序Javaprogrammers:");
sortedJavaProgrammers=javaProgrammers
.stream()
.sorted((p,p2)->(p.getSalary()-p2.getSalary()))
.collect(toList());
sortedJavaProgrammers.forEach((p)->System.out.printf("%s%s;%n",p.getFirstName(),p.getLastName()));
如果我们只对最低和最高的薪水感兴趣,比排序后选择第一个/最后一个更快的是min和max方法:
System.out.println("工资最低的Javaprogrammer:");
Personpers=javaProgrammers
.stream()
.min((p1,p2)->(p1.getSalary()-p2.getSalary()))
.get()
System.out.printf("Name:%s%s;Salary:$%,d.",pers.getFirstName(),pers.getLastName(),pers.getSalary())
System.out.println("工资最高的Javaprogrammer:");
Personperson=javaProgrammers
.stream()
.max((p,p2)->(p.getSalary()-p2.getSalary()))
.get()
System.out.printf("Name:%s%s;Salary:$%,d.",person.getFirstName(),person.getLastName(),person.getSalary())
上面的例子中我们已经看到collect方法是如何工作的。结合map方法,我们可以使用collect方法来将我们的结果集放到一个字符串,一个Set或一个TreeSet中:
System.out.println("将PHPprogrammers的firstname拼接成字符串:");
StringphpDevelopers=phpProgrammers
.stream()
.map(Person::getFirstName)
.collect(joining(";"));//在进一步的操作中可以作为标记(token)
System.out.println("将Javaprogrammers的firstname存放到Set:");
Set<String>javaDevFirstName=javaProgrammers
.stream()
.map(Person::getFirstName)
.collect(toSet());
System.out.println("将Javaprogrammers的firstname存放到TreeSet:");
TreeSet<String>javaDevLastName=javaProgrammers
.stream()
.map(Person::getLastName)
.collect(toCollection(TreeSet::new));
Streams还可以是并行的(parallel)。示例如下:
System.out.println("计算付给Javaprogrammers的所有money:");
inttotalSalary=javaProgrammers
.parallelStream()
.mapToInt(p->p.getSalary())
.sum();
我们可以使用summaryStatistics方法获得stream中元素的各种汇总数据。接下来,我们可以访问这些方法,比如getMax,getMin,getSum或getAverage:
//计算count,min,max,sum,andaveragefornumbers
List<Integer>numbers=Arrays.asList(1,2,3,4,5,6,7,8,9,10);
IntSummaryStatisticsstats=numbers
.stream()
.mapToInt((x)->x)
.summaryStatistics();
System.out.println("List中最大的数字:"+stats.getMax());
System.out.println("List中最小的数字:"+stats.getMin());
System.out.println("所有数字的总和:"+stats.getSum());
System.out.println("所有数字的平均值:"+stats.getAverage());
OK,就这样,希望你喜欢它!
总结
在本文中,我们学会了使用lambda表达式的不同方式,从基本的示例,到使用lambdas和streams的复杂示例。此外,我们还学习了如何使用lambda表达式与Comparator类来对Java集合进行排序。
本文内容总结:
原文链接:https://www.cnblogs.com/franson-2016/p/5593080.html