Java8-Lamda和Stream原理引入
# 一说明
这边文章主要是带大家为什么会有lamda表达式的出现,流式思想的产生。具体的Lamda表达式操作,Stream流会在后面的文章更新,有兴趣的朋友也可以加一下我微信公众号,分享学习干货。
二java8核心
- Lamda表达式
- 流和默认方法
- 方法引用
三引子
3.1需求
现在有批车,是中国或者英国制造的,每辆车有不同的属性,现在根据客户不同的需求挑选车。
3.2建立车的实体类
/**
* @Author lsc
* @Description <p> </p>
* @Date 2019/9/27 11:09
* @Version 1.0
*/
@Data
@AllArgsConstructor
public class Car {
// 车牌号
private String code;
// 颜色
private String color;
// 生产商
private String factory;
// 价格
private double price;
}
3.3 车辆初始化
public List<Car> InitCar(){
ArrayList<Car> carList = new ArrayList<>();
Car car1 = new Car("100", "black", "中国", 20);
Car car2 = new Car("101", "gray", "中国", 30);
Car car3 = new Car("102", "yello", "中国", 50);
Car car4 = new Car("103", "silvery", "英国", 20);
Car car5 = new Car("104", "red", "英国", 30);
carList.add(car1);
carList.add(car2);
carList.add(car3);
carList.add(car4);
carList.add(car5);
return carList;
}
3.4 客户需求实现
1 现在客户需要看看价格大于20W的车,我们以前的思想应该就是写一个方法,筛选出价格大于20W的车,对于我们so easy 对不对。
// 通过价格获取车
public List<Car> getCarByLowPrice(List<Car> carList){
ArrayList<Car> resultList = new ArrayList<>();
for (Car car : carList){
if (car.getPrice()>20){
resultList.add(car);
}
}
return resultList;
}
测试一下
// 获取 车价格 20 W以上的车
@Test
public void getCarByLowPriceTest(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取价格20W以上的车
List<Car> carByPrice1 = carFunFactory.getCarByLowPrice(cars);
System.out.println(carByPrice1);
}
输出:
[Car(code=101, color=gray, factory=中国, price=30.0), Car(code=102, color=yello, factory=中国, price=50.0), Car(code=104, color=red, factory=英国, price=30.0)]
2 现在客户需求变更想看看30W以上的车,我们就copy一下代码改个数字对把
// 通过价格获取车
public List<Car> getCarByHightPrice(List<Car> carList){
ArrayList<Car> resultList = new ArrayList<>();
for (Car car : carList){
if (car.getPrice()>30){
resultList.add(car);
}
}
return resultList;
}
测试:
// 获取 车价格 30 W以上的车
@Test
public void getCarByHightPriceTest(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取价格30W以上的车
List<Car> carByPrice1 = carFunFactory.getCarByHightPrice(cars);
System.out.println(carByPrice1);
}
3 现在有稍微一点思想的程序员就知道copy代码就因为一行不同是不是太过了,这么多重复内容,没错这时我们就知道要把价格这个参数开出来。
// 通过价格获取车
public List<Car> getCarByPrice(List<Car> carList,double price){
ArrayList<Car> resultList = new ArrayList<>();
for (Car car : carList){
if (car.getPrice()>price){
resultList.add(car);
}
}
return resultList;
}
测试:
// 根据价格获取车
@Test
public void getCarByPriceTest(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取价格30W以上的车
List<Car> carByPrice1 = carFunFactory.getCarByPrice(cars,20);
List<Car> carByPrice2 = carFunFactory.getCarByPrice(cars,30);
System.out.println(carByPrice1);
System.out.println(carByPrice2);
}
4 现在又来一位新客户它想看银色的车并且价格高于30W,还是copy一下,然后我们的思维就是再开个参数对吧。
// 通过颜色和价格获取车
public List<Car> getCarByColarAndPrce(List<Car> carList,String color,double price){
ArrayList<Car> resultList = new ArrayList<>();
for (Car car : carList){
if (color.equals(car.getColor())){
if (car.getPrice()>30){
resultList.add(car);
}
}
}
return resultList;
}
测试:
// 根据颜色和价格获取车
@Test
public void getCarByColorAndPriceTest(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为黄色,价格大于30W的车
List<Car> carByPredicate = carFunFactory.getCarByColarAndPrce(cars,"yello",30);
//[Car(code=102, color=yello, factory=中国, price=50.0)]
System.out.println(carByPredicate);
}
四 问题
如果我的车属性非常多,现在根据客户需求不断的变更,我们开的参数也会越来越多,代码里面要套很多层if判断;如果又的客户不想看一些属性的车,那么我们就不得不再copy一个方法,然后在if判定,这样的结果就会产生很多重复代码(而且就因为那几个条件不同),代码冗余,可读性很差。针对这个问题我们开始了优化之旅
五优化
1 策略模式
针对copy代码冗余和客户行为需求不断变化的问题我们可以用策略模式进行优化。
先建个接口,然后我们实现这个接口,把策略写进实现类,客户需要不同的需求,我们就调用不同的策略。
车条件判断接口:
/**
* @Author lsc
* @Description <p> </p>
* @Date 2019/9/27 14:05
* @Version 1.0
*/
public interface CarPredicate {
boolean test(Car car);
}
具体实现策略一:
/**
* @Author lsc
* @Description <p>筛选红色的车 </p>
* @Date 2019/9/27 14:08
* @Version 1.0
*/
public class CarPredicateByColor implements CarPredicate {
@Override
public boolean test(Car car) {
return "red".equals(car.getColor());
}
}
具体实现策略二:
/**
* @Author lsc
* @Description <p>获取车价格大于30W的车 </p>
* @Date 2019/9/27 14:06
* @Version 1.0
*/
public class CarPredicateByPrice implements CarPredicate {
@Override
public boolean test(Car car) {
return car.getPrice()>30;
}
}
测试根据不同的策略获取车:
@Test
public void getCarByPredicatePrice(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取价格大于30为银色的车
// 实现将条件代码封装进对象传递入参
List<Car> carByPredicate = carFunFactory.getCarByPredicate(cars,new CarPredicateByPrice());
// [Car(code=102, color=yello, factory=中国, price=50.0)]
System.out.println(carByPredicate);
}
@Test
public void getCarByPredicateColor(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
// 实现将条件代码封装进对象传递入参
List<Car> carByPredicate = carFunFactory.getCarByPredicate(cars,new CarPredicateByColor());
// [Car(code=104, color=red, factory=英国, price=30.0)]
System.out.println(carByPredicate);
}
策略模式已经很好的解决了copy代码冗余的问题,但随着客户需要的不断变更,我们需要很多的实现类,感觉还是有点麻烦。
2 匿名类
针对客户需求不断变化我们使用策略模式要写很多实现类的问题,可以用匿名类解决。
@Component
public class CarFunFactory {
// 新增通过函数式接口获得车辆
public List<Car> getCarByPredicate(List<Car> carList, CarPredicate carPredicate){
ArrayList<Car> resultList = new ArrayList<>();
for (Car car : carList){
if (carPredicate.test(car)){
resultList.add(car);
}
}
return resultList;
}
}
@Test
public void getCarByPredicatByAnonymous(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
List<Car> carByPrice1 = carFunFactory.getCarByPredicate(cars, new CarPredicate() {
@Override
public boolean test(Car car) {
return car.getPrice()>30;
}
});
// [Car(code=102, color=yello, factory=中国, price=50.0)]
System.out.println(carByPrice1);
}
3 Lamda表达式
从当初的copy方法到策略模式,在到匿名内部类是不是已经简化了许多代码呢?答案是肯定的,但许多程序员包括我在内都不是很喜欢匿名类,因为它不易理解,还占用很多空间资源。于是Java8引入了Lamda表达式。
public void getCarByPredicatByLamda(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
List<Car> carByPrice1 = carFunFactory.getCarByPredicate(cars,car -> car.getPrice()>30);
// [Car(code=102, color=yello, factory=中国, price=50.0)]
System.out.println(carByPrice1);
}
这边讲解一下Lamda表示语法,具体内容在后面文章发布。
示例:car -> car.getPrice()>30
解析示例格式:
car 是参数列表;
-> 是箭头
car.getPrice()>30 是方法主体
Lamda语法格式:
- 这是我们的简写模式(方法主体为一句表达式): (参数列表)-> 方法主体
- 非简写模式:(参数列表)-> {方法主体;}
说明:有时候无参那么就是 () -> 方法主体
4 Stream流
有了Lamda表达式做优化已经非常简练直观,但java8还有个思想就是流式思想,其内置很多默认方法,省去了我们自己建立判断接口。
@Test
public void getCarByPredicatByStream(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
List<Car> collect = cars.stream().filter(car -> car.getPrice() > 30).collect(Collectors.toList());
// [Car(code=102, color=yello, factory=中国, price=50.0)]
System.out.println(collect);
}
六 再看看我们平时用到Lamda的例子
1排序
// 匿名内部类方式对车编号进行排序
@Test
public void getCarSortByAnonymous(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
cars.sort(new Comparator<Car>() {
@Override
public int compare(Car o1, Car o2) {
return o1.getCode().compareTo(o2.getCode());
}
});
System.out.println(cars);
}
// 使用 lamda表达式对车编码进行排序
@Test
public void getCarSortByLamda(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
cars.sort((o1, o2) -> o1.getCode().compareTo(o2.getCode()));
System.out.println(cars);
}
// 使用 Stream API 对车编号进行排序
@Test
public void getCarSortByStream(){
// 初始化车信息
List<Car> cars = carFunFactory.InitCar();
// 获取颜色为红色的车
List<Car> collect = cars.stream().sorted((o1, o2) -> o1.getCode().compareTo(o2.getCode())).collect(Collectors.toList());
System.out.println(collect);
}
2 runnable
@Test
public void runnableTest(){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello world youku1327");
}
});
thread.start();
}
@Test
public void runnableLamdaTest(){
Thread thread = new Thread(() -> System.out.println("hello world youku1327"));
thread.start();
}
七 总结
我们是将条件行为代码封装进了对象里面,然后将对象传递给特定的方法进行筛选出我们的需求,我们称这种操作为行为参数化
.
之前的策略模式接口(CarPredicate)只有一个抽象方法,我们称这种接口为函数式接口,通常规范的函数式接口会带上@FunctionalInterface注解;
java8还有个特性就是如果你的接口中没有抽象方法,但有个默认(default)方法,它也是函数式接口。
所有的匿名类实现方式我们都可以用Lamda表达式替换,Lamda表达式等于就是匿名类的简化。
八 致谢
作者坚持原创,分享干货技术,更新会有点慢;感谢观看我的文章,不足之处可以指出,如果想看更多好文章或者其他文章的源码可以关注我的微信公众号。