文章目录
Lambda
01 Hello Lambda
1.1 普通方法
public class Hero implements Comparable<Hero>{
public String name;
public float hp;
public int damage;
public Hero(){
}
public Hero(String name) {
this.name =name;
}
//初始化name,hp,damage的构造方法
public Hero(String name,float hp, int damage) {
this.name =name;
this.hp = hp;
this.damage = damage;
}
@Override
public int compareTo(Hero anotherHero) {
if(damage<anotherHero.damage)
return 1;
else
return -1;
}
@Override
public String toString() {
return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]\r\n";
}
}
使用一个普通方法,在for循环遍历中进行条件判断,筛选出满足条件的数据。
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<10;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化前的集合:");
System.out.println(heros);
System.out.println("filter方法筛选出hp>100 && damage<50的集合:");
System.out.println(filter(heros));
private static List<Hero> filter(List<Hero> heros){
List<Hero> filterHero=new ArrayList<>();
for(Hero h:heros) {
if(h.hp>100&&h.damage<50)
filterHero.add(h);
}
return filterHero;
}
1.2 匿名类方式
首先准备一个接口HeroChecker,提供一个test(Hero)方法
然后通过匿名类的方式,实现这个接口
HeroChecker checker = new HeroChecker() {
public boolean test(Hero h) {
return (h.hp>100 && h.damage<50);
}
};
接着调用filter,传递这个checker进去进行判断,这种方式就很像通过Collections.sort在对一个Hero集合排序,需要传一个Comparator的匿名类对象进去一样。
public interface HeroChecker {
public boolean test(Hero h) ;
}
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class TestLambda{
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<10;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化前的集合:");
System.out.println(heros);
System.out.println("filter方法筛选出hp>100 && damage<50的集合:");
System.out.println(filter(heros));
HeroChecker heroChecker=new HeroChecker() {
@Override
public boolean test(Hero h) {
// TODO Auto-generated method stub
return (h.hp>100&&h.damage<50);
}
};
System.out.println("匿名类方法筛选出hp>100 && damage<50的集合:");
System.out.println(filter(heros,heroChecker));
}
private static List<Hero> filter(List<Hero> heros){
List<Hero> filterHero=new ArrayList<>();
for(Hero h:heros) {
if(h.hp>100&&h.damage<50)
filterHero.add(h);
}
return filterHero;
}
private static List<Hero> filter(List<Hero> heros,HeroChecker checker) {
List<Hero> filterHero=new ArrayList<>();
for(Hero h:heros) {
if(checker.test(h))
filterHero.add(h);
}
return filterHero;
}
}
1.3 Lambda表达式
使用Lambda方式筛选出数据
filter(heros,(h)->h.hp>100 && h.damage<50);
同样是调用filter方法,从上一步的传递匿名类对象,变成了传递一个Lambda表达式进去
h->h.hp>100 && h.damage<50
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import charactor.Hero;
public class TestLamdba {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 5; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
System.out.println("使用Lamdba的方式,筛选出 hp>100 && damange<50的英雄");
filter(heros,h->h.hp>100 && h.damage<50);
}
private static void filter(List<Hero> heros,HeroChecker checker) {
for (Hero hero : heros) {
if(checker.test(hero))
System.out.print(hero);
}
}
}
1.4 匿名类演变Lambda表达式
Lambda表达式可以看成是匿名类一点点演变过来
- 匿名类的正常写法
HeroChecker c1 = new HeroChecker() {
public boolean test(Hero h) {
return (h.hp>100 && h.damage<50);
}
};
- 把外面的壳子去掉
只保留方法参数和方法体
参数和方法体之间加上符号 ->
HeroChecker c2 = (Hero h) ->{
return h.hp>100 && h.damage<50;
};
- 把return和{}去掉
HeroChecker c3 = (Hero h) ->h.hp>100 && h.damage<50;
- 把 参数类型和圆括号去掉(只有一个参数的时候,才可以去掉圆括号)
HeroChecker c4 = h ->h.hp>100 && h.damage<50;
- 把c4作为参数传递进去
filter(heros,c4);
- 直接把表达式传递进去
filter(heros, h -> h.hp > 100 && h.damage < 50);
1.5 匿名方法
与匿名类 概念相比较,
Lambda 其实就是匿名方法,这是一种把方法作为参数进行传递的编程思想。
虽然代码是这么写
filter(heros, h -> h.hp > 100 && h.damage < 50);
但是,Java会在背后,悄悄的,把这些都还原成匿名类方式。
引入Lambda表达式,会使得代码更加紧凑,而不是各种接口和匿名类到处飞。
1.6 Lambda弊端
Lambda表达式虽然带来了代码的简洁,但是也有其局限性。
- 可读性差,与啰嗦的但是清晰的匿名类代码结构比较起来,Lambda表达式一旦变得比较长,就难以理解
- 不便于调试,很难在Lambda表达式中增加调试信息,比如日志
- 版本支持,Lambda表达式在JDK8版本中才开始支持,如果系统使用的是以前的版本,考虑系统的稳定性等原因,而不愿意升级,那么就无法使用。
Lambda比较适合用在简短的业务代码中,并不适合用在复杂的系统中,会加大维护成本。
1.7 总结
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class TestLambda{
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<10;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化前的集合:");
System.out.println(heros);
System.out.println("filter方法筛选出hp>100 && damage<50的集合:");
System.out.println(filter(heros));
HeroChecker heroChecker=new HeroChecker() {
@Override
public boolean test(Hero h) {
// TODO Auto-generated method stub
return (h.hp>100&&h.damage<50);
}
};
System.out.println("匿名类方法筛选出hp>100 && damage<50的集合:");
System.out.println(filter(heros,heroChecker));
//Lambda表达式不需要定义匿名类,直接调用filter(List<Hero> heros,HeroChecker checker)方法
System.out.println("Lambda方法筛选出hp>100 && damage<50的集合:");
System.out.println(filter(heros,(h)->h.hp>100 && h.damage<50));
System.out.println("Lambda方法排序");
/*
Collections.sort(heros,(h1,h2)->{
if(h1.hp>=h2.hp)
return 1;
else
return -1;
});
*/
//匿名类
Comparator<Hero> c = new Comparator<Hero>() {
@Override
public int compare(Hero h1, Hero h2) {
return h1.hp>=h2.hp?1:-1;
}
};
//排序不同写法
Collections.sort(heros,(h1, h2)->h1.hp>=h2.hp?1:-1);
Collections.sort(heros,(h1,h2)-> (int)(h1.hp - h2.hp));
System.out.println(heros);
//筛选hp>100 && damage<50
// System.out.println("筛选出hp>100 && damage<50的集合:");
// for(Hero h:heros) {
// if(h.hp>100&&h.damage<50)
// System.out.println(h);
// }
}
private static List<Hero> filter(List<Hero> heros){
List<Hero> filterHero=new ArrayList<>();
for(Hero h:heros) {
if(h.hp>100&&h.damage<50)
filterHero.add(h);
}
return filterHero;
}
private static List<Hero> filter(List<Hero> heros,HeroChecker checker) {
List<Hero> filterHero=new ArrayList<>();
for(Hero h:heros) {
if(checker.test(h))
filterHero.add(h);
}
return filterHero;
}
}
02 方法引用
public interface HeroChecker {
public boolean test(Hero h) ;
}
public class Hero implements Comparable<Hero>{
public String name;
public float hp;
public int damage;
public Hero(){
}
public Hero(String name) {
this.name =name;
}
//初始化name,hp,damage的构造方法
public Hero(String name,float hp, int damage) {
this.name =name;
this.hp = hp;
this.damage = damage;
}
@Override
public int compareTo(Hero anotherHero) {
if(damage<anotherHero.damage)
return 1;
else
return -1;
}
@Override
public String toString() {
return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]\r\n";
}
public boolean matched(){
return this.hp>100 && this.damage<50;
}
}
public class TestLambda{
public static boolean testHero(Hero h) {
return h.hp>100&&h.damage<50;
}
public boolean testHero1(Hero h) {
return h.hp>100&&h.damage<50;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<5;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化前的集合:");
System.out.println(heros);
HeroChecker heroChecker=new HeroChecker() {
@Override
public boolean test(Hero h) {
// TODO Auto-generated method stub
return h.hp>100&&h.damage<50;
}
};
System.out.println("匿名类过滤");
System.out.println(filter(heros,heroChecker));
System.out.println("Lambda表达式过滤");
System.out.println(filter(heros,h->h.hp>100&&h.damage<50));
//1. 引用静态方法testHero(): 类名.方法(参数) 或 类名::方法
System.out.println("Lambda表达式使用静态方法过滤");
System.out.println(filter(heros,h->TestLambda.testHero(h)));
System.out.println("直接引用静态方法过滤");
System.out.println(filter(heros,TestLambda::testHero));
//2.引用非静态方法testHero1: 对象名.方法
TestLambda testLambda=new TestLambda();
System.out.println("引用对象方法过滤");
System.out.println(filter(heros,testLambda::testHero1));
//3.引用对象容器的matched方法: 对象容器的类::对象容器的方法
System.out.println("引用对象容器的matched方法");
System.out.println(filter(heros,h->h.matched()));
System.out.println(filter(heros,Hero::matched));
//4.引用构造器:
Supplier<List> s=new Supplier<List>() {
@Override
public List get() {
// TODO Auto-generated method stub
return new ArrayList();
}
};
//(1)匿名类
List list1=s.get();
//(2)Lambda表达式
List list2=getList(()->new ArrayList());
//(3)引用构造器
List list3=getList(ArrayList::new);
}
public static List getList(Supplier<List> s) {
return s.get();
}
private static List<Hero> filter(List<Hero> heros,HeroChecker checker) {
List<Hero> filterHero=new ArrayList<>();
for(Hero h:heros) {
if(checker.test(h))
filterHero.add(h);
}
return filterHero;
}
2.1 引用静态方法
静态方法不依赖对象的存在:
- 在Lambda表达式中调用这个静态方法:
filter(heros, h -> TestLambda.testHero(h) );
- 调用静态方法还可以改写为:
filter(heros, TestLambda::testHero);
2.2 引用对象方法
与引用静态方法很类似,只是传递方法的时候,依赖一个对象的存在
TestLambda testLambda = new TestLambda();
filter(heros, testLambda::testHero);
2.3 引用容器中对象的方法
- 在Lambda表达式中调用容器中的对象Hero的方法matched
filter(heros,h-> h.matched() );
- matched恰好就是容器中的对象Hero的方法,那就可以进一步改写为
filter(heros, Hero::matched);
2.4 引用构造器
有的接口中的方法会返回一个对象,比如java.util.function.Supplier提供
了一个get方法,返回一个对象。
public interface Supplier<T> {
T get();
}
设计一个方法,参数是这个接口
public static List getList(Supplier<List> s){
return s.get();
}
为了调用这个方法,有3种方式:
- 匿名类
- Lambda表达式
- 引用构造器
练习之引用静态方法
把比较器-Comparator 章节中的代码,使用引用静态方法的方式来实现.
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
/*
*@Author hmg
*@Description
*@Time 2019年12月20日下午8:07:02
*@Version1.0
*/
public class TestCollection {
public static int compareNumber(Hero h1, Hero h2) {
return h1.hp<=h2.hp?-1:1;
}
public static void main(String[] args) {
Random r =new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 10; i++) {
//通过随机值实例化hero的hp和damage
heros.add(new Hero("hero "+ i, r.nextInt(100), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
//引入Comparator,指定比较的算法
// Comparator<Hero> c = new Comparator<Hero>() {
// @Override
// public int compare(Hero h1, Hero h2) {
// //按照hp进行排序
// if(h1.hp>=h2.hp)
// return 1; //正数表示h1比h2要大
// else
// return -1;
// }
// };
// Collections.sort(heros,c);
Collections.sort(heros,TestCollection::compareNumber);
System.out.println("按照血量排序后的集合:");
System.out.println(heros);
}
}
练习之引用容器中的对象的方法
把比较器-Comparator 章节中的代码,使用 引用容器中的对象的方法 的方式来实现。
提示: 为Hero提供一个compareHero方法
//为Hero增加compareHero方法
public int compareHero(Hero h){
return hp>=h.hp?-1:1;
}
//Lambda表达式
Comparator<Hero> c = (h1,h2)-> h1.compareHero(h2) ;
Collections.sort(heros,c);
//引用构造器
Collections.sort(heros,Hero::compareHero);
或者
//为Hero增加compareHero方法
public int compareHero(Hero h1,Hero h2){
return h1.hp<=h2.hp?-1:1;
}
//虽不知为什么,但这个可以用
Hero h=new Hero();
Collections.sort(heros,(h1,h2)->h.compareHero(h1,h2));
练习之引用构造器
package collection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;
public class TestCollection {
public static void main(String[] args) {
insertFirst(ArrayList::new, "ArrayList");
insertFirst(LinkedList::new, "LinkedList");
}
private static void insertFirst(Supplier<List> s, String type) {
int total = 1000 * 100;
final int number = 5;
long start = System.currentTimeMillis();
List list = s.get();
for (int i = 0; i < total; i++) {
list.add(0, number);
}
long end = System.currentTimeMillis();
System.out.printf("在%s 最前面插入%d条数据,总共耗时 %d 毫秒 %n", type, total, end - start);
}
}
03 聚合操作
3.1 遍历数据(传统方式和聚合操作)
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/*
*@Author hmg
*@Description
*@Time 2019年12月19日下午7:36:43
*@Version1.0
*/
public class TestLambda{
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<5;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化前的集合:");
System.out.println(heros);
//传统for循环
System.out.println("传统for循环");
for(Hero h:heros) {
if(h.hp>100&&h.damage<50)
System.out.println(h);
}
//聚合操作
System.out.println("聚合操作");
heros
.stream()
.filter(h->h.hp > 100 && h.damage < 50)
.forEach(h->System.out.println(h));
}
}
3.2 Stream和管道的概念
要了解聚合操作,首先要建立Stream和管道的概念
Stream 和Collection结构化的数据不一样,Stream是一系列的元素,就像是生产线上的罐头一样,一串串的出来。
管道指的是一系列的聚合操作。
管道又分3个部分:
- 管道源:在这个例子里,源是一个List
- 中间操作: 每个中间操作,又会返回一个Stream,比如.filter()又返回一个Stream, 中间操作是“懒”操作,并不会真正进行遍历。
- 结束操作:当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。 结束操作不会返回Stream,但是会返回int、float、String、 Collection或者像forEach,什么都不返回, 结束操作才进行真正的遍历行为,在遍历的时候,才会去进行中间操作的相关判断
3.3 管道源
把Collection切换成管道源很简单,调用stream()就行了。
heros.stream()
但是数组却没有stream()方法,需要使用
Arrays.stream(hs)
或者Stream.of(hs)
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 5; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
//管道源是集合
heros
.stream()
.forEach(h->System.out.println(h.name));
//管道源是数组
Hero hs[] = heros.toArray(new Hero[heros.size()]);
Arrays.stream(hs)
.forEach(h->System.out.println(h.name));
}
3.4 中间操作
每个中间操作,又会返回一个Stream,比如.filter()又返回一个Stream, 中间操作是“懒”操作,并不会真正进行遍历。
中间操作比较多,主要分两类:对元素进行筛选 和 转换为其他形式的流
- 对元素进行筛选:
filter 匹配
distinct 去除重复(根据equals判断)
sorted 自然排序
sorted(Comparator) 指定排序
limit 保留
skip 忽略 - 转换为其他形式的流
mapToDouble 转换为double的流
map 转换为任意类型的流
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/*
*@Author hmg
*@Description
*@Time 2019年12月19日下午7:36:43
*@Version1.0
*/
public class TestLambda{
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<5;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
heros.add(heros.get(0));
System.out.println("初始化前的集合(最后一个元素重复):");
System.out.println(heros);
System.out.println("满足hp>100&&damage<50的数据");
heros
.stream()
.filter(h->h.hp>100&&h.damage<50)
.forEach(h->System.out.println(h));
System.out.println("去除重复的数据,去除的标准是看equals");
heros
.stream()
.distinct()
.forEach(h->System.out.println(h));
System.out.println("按照血量排序");
heros
.stream()
.sorted((h1,h2)->h1.hp<=h2.hp?-1:1)
.forEach(h->System.out.println(h));
System.out.println("保留三个");
heros
.stream()
.limit(3)
.forEach(h->System.out.println(h));
System.out.println("忽略前三个");
heros
.stream()
.skip(3)
.forEach(h->System.out.println(h));
System.out.println("转换为doubel的stream");
heros
.stream()
.mapToDouble(Hero::getHp)
.forEach(h->System.out.println(h));
System.out.println("转换为任意类型的stream");
heros
.stream()
.map((h)->h.name+"-"+h.hp+"-"+h.damage)
.forEach(h->System.out.println(h));
}
}
3.5 结束操作
当进行结束操作后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。 结束操作不会返回Stream,但是会返回int、float、String、 Collection或者像forEach,什么都不返回,。
结束操作才真正进行遍历行为,前面的中间操作也在这个时候,才真正的执行。
- 常见结束操作如下:
forEach() 遍历每个元素
toArray() 转换为数组
min(Comparator) 取最小的元素
max(Comparator) 取最大的元素
count() 总数
findFirst() 第一个元素
package lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/*
*@Author hmg
*@Description
*@Time 2019年12月19日下午7:36:43
*@Version1.0
*/
public class TestLambda{
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<5;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化前的集合");
heros
.stream()
.forEach(h->System.out.println(h));
System.out.println("返回一个数组");
Object[] hs=heros
.stream()
.toArray();
System.out.println(Arrays.toString(hs));
System.out.println("返回伤害最低的那个英雄");
Hero minDamageHero=
heros
.stream()
.min((h1,h2)-> h1.damage-h2.damage)
.get();
System.out.println(minDamageHero);
System.out.println("返回伤害最高的那个英雄");
Hero maxDamageHero=
heros
.stream()
.max((h1,h2)->h1.damage-h2.damage)
.get();
System.out.println(maxDamageHero);
System.out.println("流中数据的总数");
long totalOfStream=
heros
.stream()
.count();
System.out.println(totalOfStream);
System.out.println("第一个英雄");
Hero first=
heros
.stream()
.findFirst()
.get();
System.out.println(first);
}
}
3.6 练习之聚合操作
首选准备10个Hero对象,hp和damage都是随机数。
分别用传统方式和聚合操作的方式,把hp第三高的英雄名称打印出来.
package lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
/*
*@Author hmg
*@Description
*@Time 2019年12月19日下午7:36:43
*@Version1.0
*/
public class TestLambda{
public static void main(String[] args) {
// TODO Auto-generated method stub
Random r=new Random();
List<Hero> heros=new ArrayList<Hero>();
for(int i=0;i<10;i++) {
heros.add(new Hero("hero "+i,r.nextInt(1000),r.nextInt(100)));
}
System.out.println("初始化");
heros
.stream()
.forEach(h->System.out.println(h));
System.out.println("传统方式");
List<Hero> comparatorHeros=new ArrayList<Hero>();
comparatorHeros.addAll(heros);
Comparator<Hero> comp=new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
// TODO Auto-generated method stub
return o1.hp<=o2.hp?1:-1;
}
};
Collections.sort(comparatorHeros,comp);
System.out.println(comparatorHeros.get(2));
System.out.println("聚合操作");
String s=
heros
.stream()
.sorted((h1,h2)-> h1.hp<=h2.hp ?1:-1)
.skip(2)
.map(h->h.name)
.findFirst()
.get();
System.out.println(s);
}
}
本文学习内容均来自how2j.cn,用于个人笔记和总结,想学习的可以到该网站学习哦,侵删。