JDK 1.8新特性Lambda入门
网上关于java lambda的例子很多,但是这些五花八门的例子很不常见不常用,最后导致初学者望而却步,其实我们抓住lambda的本质之后,会发现lambda还是很好理解的。毕竟,java8设计lambda的初衷是给开发者提供便利,而不是制造障碍。 lambda表达式本质是匿名方法,下面是一些lambda表达式: (int x, int y) -> x + y () -> 42 (String s) -> { System.out.println(s); } 第一个lambda表达式接收x和y这两个整形参数并返回它们的和; 第二个lambda表达式不接收参数,返回整数42; 第三个lambda表达式接收一个字符串并把它打印到控制台,不返回值。 lambda表达式的语法由参数列表、箭头符号->和函数体组成。函数体既可以是一个表达式,也可以是一个语句块: 表达式:表达式会被执行然后返回执行结果。 语句块:语句块中的语句会被依次执行,就像方法中的语句一样。 return语句会把控制权交给匿名方法的调用者 break和continue只能在循环中使用 如果函数体有返回值,那么函数体内部的每一条路径都必须返回值 java8的安装 工欲善其器必先利其器,首先安装JDK8。过程省略,大家应该都可以自己搞定。但是有一点这里强调一下(Windows系统):目前我们工作的版本一般是java 6或者java 7,所以很多人安装java8基本都是学习为主。这样就在自己的机器上会存在多版本的JDK。而且大家一般是希望在命令行中执行java命令是基于老版本的jdk。但是在安装完jdk8并且没有设置path的情况下,你如果在命令行中输入:java -version,屏幕上会显示是jdk 8。这是因为jdk8安装的时候,会默认在C:/Windows/System32中增加java.exe,这个调用的优先级比path设置要高。所以即使path里指定是老版本的jdk,但是执行java命令显示的依然是新版本的jdk。这里我们要做的就是删除C:/Windows/System32中的java.exe文件(不要手抖!)。 下面转自:http://blog.csdn.net/renfufei/article/details/24600507 Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda表达式还增强了集合库。 Java SE 8添加了2个对集合数据进行批量操作的包:java.util.function包以及java.util.stream包。 流(stream)就如同迭代器(iterator),但附加了许多额外的功能。 总的来说,lambda表达式和 stream 是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。 在本文中,我们将从简单到复杂的示例中见认识lambda表达式和stream的强悍。 环境准备 如果还没有安装Java 8,那么你应该先安装才能使用lambda和stream(译者建议在虚拟机中安装,测试使用)。 像NetBeans 和IntelliJ IDEA 一类的工具和IDE就支持Java 8特性,包括lambda表达式,可重复的注解,紧凑的概要文件和其他特性。 下面是Java SE 8和NetBeans IDE 8的下载链接:Java Platform (JDK 8): 从Oracle下载Java 8,也可以和NetBeans IDE一起下载NetBeans IDE 8: 从NetBeans官网下载NetBeans IDE Lambda表达式的语法 基本语法: (parameters) -> expression 或 (parameters) ->{ statements; } 下面是Java lambda表达式的简单例子: [java]view plaincopy //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 循环")来遍历,在Java SE 8中可以转换为另一种形式: [java]view plaincopy 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接口时也可以这样使用: [java]view plaincopy //使用匿名内部类 btn.setOnAction(newEventHandler<ActionEvent>(){ @Override publicvoidhandle(ActionEventevent){ System.out.println("HelloWorld!"); } }); //或者使用lambdaexpression btn.setOnAction(event->System.out.println("HelloWorld!")); 下面是使用lambdas 来实现 Runnable接口 的示例: [java]view plaincopy //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。 使用旧的方式,代码如下所示: [java]view plaincopy 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,可以通过下面的代码实现同样的功能: [java]view plaincopy //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 : [java]view plaincopy //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类: [java]view plaincopy 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对象: [java]view plaincopy 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方法来迭代输出上述列表: [java]view plaincopy 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%: [java]view plaincopy 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程序员: [java]view plaincopy System.out.println("下面是月薪超过$1,400的PHP程序员:") phpProgrammers.stream() .filter((p)->(p.getSalary()>1400)) .forEach((p)->System.out.printf("%s%s;",p.getFirstName(),p.getLastName())); 我们也可以定义过滤器,然后重用它们来执行其他操作: [java]view plaincopy //定义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方法,可以限制结果集的个数: [java]view plaincopy 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中,然后显示列表: [java]view plaincopy 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方法: [plain]view plaincopy 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中: [java]view plaincopy 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)。 示例如下: [java]view plaincopy System.out.println("计算付给Javaprogrammers的所有money:"); inttotalSalary=javaProgrammers .parallelStream() .mapToInt(p->p.getSalary()) .sum(); 我们可以使用summaryStatistics方法获得stream 中元素的各种汇总数据。 接下来,我们可以访问这些方法,比如getMax, getMin, getSum或getAverage: [java]view plaincopy //计算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集合进行排序。