函数式编程以及Lambda表达式
函数式编程
需求
有一些商品信息,现在需要将这些商品添加到购物车中获取购物车中的商品信息,然后完成以下需求.
商品实体类
package lambda.cart;
/**
* 下单商品信息对象
*/
public class Sku {
// 商品编号
private Integer skuId;
// 商品名称
private String skuName;
// 商品单价
private Double skuPrice;
// 商品个数
private Integer totalNum;
// 商品总价
private Double totalPrice;
// 商品分类
private Enum skuCategory;
/**
* 有参构造
* @param skuId
* @param skuName
* @param skuPrice
* @param totalNum
* @param totalPrice
* @param skuCategory
*/
public Sku(Integer skuId, String skuName, Double skuPrice, Integer totalNum, Double totalPrice, Enum skuCategory) {
this.skuId = skuId;
this.skuName = skuName;
this.skuPrice = skuPrice;
this.totalNum = totalNum;
this.totalPrice = totalPrice;
this.skuCategory = skuCategory;
}
public Integer getSkuId() {
return skuId;
}
public String getSkuName() {
return skuName;
}
public Double getSkuPrice() {
return skuPrice;
}
public Integer getTotalNum() {
return totalNum;
}
public Double getTotalPrice() {
return totalPrice;
}
public Enum getSkuCategory() {
return skuCategory;
}
}
商品分类枚举
package lambda.cart;
/**
* 商品分类枚举
*/
public enum SkuCategoryEnum {
CLOTHING(10, "服装"),
DIGITAL(20, "数码产品"),
SPORTS(30, "运动类"),
BOOKS(40, "书籍类");
// 商品类型的编号
private Integer code;
// 商品类型的名称
private String name;
/**
* 构造方法
*
* @param code
* @param name
*/
SkuCategoryEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
}
添加所有商品信息到购物车中
private static List cartSkuList = new ArrayList() {
{
add(new Sku(654032, "无人机", 22.2, 2, 44.4, SkuCategoryEnum.DIGITAL));
add(new Sku(654033, "VR眼镜", 11.1, 2, 22.2, SkuCategoryEnum.DIGITAL));
add(new Sku(654034, "充气娃娃", 1000.0, 2, 2000.0, SkuCategoryEnum.SPORTS));
add(new Sku(654035, "Java核心技术", 50.0, 2, 100.0, SkuCategoryEnum.BOOKS));
add(new Sku(654036, "Python编程", 49.0, 2, 96.0, SkuCategoryEnum.BOOKS));
add(new Sku(654037, "女装", 66.0, 1, 66.0, SkuCategoryEnum.CLOTHING));
}
};
获取购物车中的所有商品
public static List getCartSkuList() {
return cartSkuList;
}
需求分析和完成
找出购物车中所有属于电子数码的商品
- 实现方式:使用循环遍历购物车中的所有商品信息,然后使用枚举进行比对
public static List filterElectronicsSku(List cartSkuList) {
List result = new ArrayList();
for (Sku sku : cartSkuList
) {
if (SkuCategoryEnum.DIGITAL.equals(sku.getSkuCategory())) {
result.add(sku);
}
}
return result;
}
根据商品类型查找商品
- 实现方式:将商品类别枚举作为参数传递给方法,使用商品分类枚举进行比对
/**
* 根据商品类型查找商品
* Version:2.0(单一维度条件参数化)
*
* @param cartSkuList
* @param skuCategoryEnum
* @return
*/
public static List filterSkuByCategory(List cartSkuList, SkuCategoryEnum skuCategoryEnum) {
List result = new ArrayList();
for (Sku sku : cartSkuList
) {
if (skuCategoryEnum.equals(sku.getSkuCategory())) {
result.add(sku);
}
}
return result;
}
通过商品类型或总价过滤商品
/**
* 通过商品类型或总价过滤商品
* Version: 3.0
*
* @param cartSkuList
* @param categoryEnum
* @param totalPrice
* @param categoryOrPrice false:使用价格过滤,true使用分类
* @return
*/
public static List filterSku(
List cartSkuList,
SkuCategoryEnum categoryEnum,
Double totalPrice,
Boolean categoryOrPrice
) {
List result = new ArrayList();
for (Sku s : cartSkuList
) {
if (
(categoryOrPrice && categoryEnum.equals(s.getSkuCategory()))
|| (!categoryOrPrice && s.getTotalPrice() > totalPrice)
) {
result.add(s);
}
}
return result;
}
根据不同的商品判断标准去过滤商品
实现方式:定义一个函数式接口,然后定义多个实现类,分别对应多个筛选条件.
函数式接口
有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。
package lambda.cart;
/**
* Sku选择谓词接口
*/
public interface SkuPredicate {
/**
* 选择判断标准
* @param sku
* @return
*/
boolean test(Sku sku);
}
根据商品是否为图书分类进行筛选
package lambda.cart;
/**
* 该商品是否为图书分类
*/
public class SkuBooksCategoryPredicate implements SkuPredicate {
@Override
public boolean test(Sku sku) {
return SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory());
}
}
根据商品的总价过滤商品
package lambda.cart;
/**
* 根据商品的总价过滤商品
*/
public class SkuTotalPricePredicate implements SkuPredicate {
@Override
public boolean test(Sku sku) {
return sku.getTotalPrice() > 2000;
}
}
需求实现
将函数式接口作为参数传给方法,然后调用函数式接口的test方法.接口有不同的实现类根据传递不同的实现类进行调用.
方法定义
/**
* Version: 4.0
* 根据不同的商品判断标准
*
* @param cartSkuList 商品列表
* @param skuPredicate 不同的商品属性判断标准
* @return
*/
public static List filterSku(List cartSkuList, SkuPredicate skuPredicate) {
List result = new ArrayList();
for (Sku s : cartSkuList
) {
if (skuPredicate.test(s)) {
result.add(s);
}
}
return result;
}
实体类的调用方法
/**
* 将判断逻辑参数化之实体类
*/
@Test
public void filterSku2() {
List cartSkuList = CartService.getCartSkuList();
List skus = CartService.filterSku(cartSkuList, new SkuBooksCategoryPredicate());
System.out.println(JSON.toJSONString(skus, true));
}
匿名类的调用方法
/**
* 将判断逻辑参数化之 匿名类
*/
@Test
public void filterSku3() {
List cartSkuList = CartService.getCartSkuList();
List skus = CartService.filterSku(cartSkuList, new SkuPredicate() {
@Override
public boolean test(Sku sku) {
return sku.getSkuPrice() >= 1000;
}
});
System.out.println(JSON.toJSONString(skus, true));
}
Lambda方法进行调用
/**
* 将判断逻辑参数化之 Lambda
*/
@Test
public void filterSku4() {
List cartSkuList = CartService.getCartSkuList();
List skus = CartService.filterSku(cartSkuList, (Sku sku) -> sku.getSkuPrice() >= 1000);
System.out.println(JSON.toJSONString(skus, true));
}
使用函数式接口处理文件内容|文件控制台打印
第一:自定义一个函数式接口
FileConsumer
/**
* Created by 肖橙橙
* CreateTime 2020-07-27 14:09
* 文件处理函数式接口
*/
@FunctionalInterface
public interface FileConsumer {
/**
* 抽象方法
* 接收的是一个文件对象
* @param fileContent 文件内容的字符串
*/
void FileHandler(String fileContent);
}
第二:创建一个文件服务类
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
/**
* Created by 肖橙橙
* CreateTime 2020-07-27 14:08
* 文件服务类
*/
public class FileService {
/**
* 通过URL获取本地内容,调用函数式接口处理
* @param url
* @param fileConsumer
*/
public void fileHandler(String url, FileConsumer fileConsumer) throws Exception {
//创建文件读取流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(url)));
//定义一个行变量和内容sb
String line;
StringBuilder sb = new StringBuilder();
//循环读取文件内容
while ((line = bufferedReader.readLine())!=null){
//如果不为null,则将该行添加
sb.append(line+"n");
}
//调用函数式接口方法,将文件内容传递给lambda表达式
fileConsumer.FileHandler(sb.toString());
}
}
第三:编写一个测试类
public class FileServiceTest {
@Test
public void fileHandler() throws Exception {
//通过lambda表达式打印文件内容
FileService fileService = new FileService();
fileService.fileHandler("填写文件的绝对路径地址",fileContent-> System.out.println(fileContent));
}
常用JDK函数式接口
方法引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。
双冒号 ::
为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
参考文档:blog.csdn.net/m0_46502538…
流编程
短路操作:找到符合条件的元素后,不再执行。
非短路操作:每个元素执行一次
无状态操作: 每个元素可以单独执行
有状态操作: 需要建立在所有元素基础上执行
filter
list.stream().filter(sku -> SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())).forEach(
item ->{
System.out.println(JSON.toJSONString(item,true));
}
);
map
list.stream().map(sku -> sku.getSkuName()).forEach(
item -> System.out.println(JSON.toJSONString(item,true))
flatMap
list.stream()
.flatMap(sku-> Arrays.stream(sku.getSkuName().split("")))
.forEach( item -> System.out.println(JSON.toJSONString(item,true)) );
peek
list
.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
.forEach( item -> System.out.println(JSON.toJSONString(item,true)) );
sort
list
.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.forEach( item -> System.out.println(JSON.toJSONString(item,true)) );
distinct
对流中元素进行去重
list
.stream()
.map(Sku::getSkuCategory)
.distinct()
.forEach( item -> System.out.println(JSON.toJSONString(item,true)) );
skip
# 总价格按照从低到高排序,并跳过前3个
list
.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.skip(3)
.forEach( item -> System.out.println(JSON.toJSONString(item,true)) );
limit
截断前n条记录
list
.stream()
.sorted(Comparator.comparing(Sku::getTotalPrice))
.limit(5)
.forEach( item -> System.out.println(JSON.toJSONString(item,true)) );
AllMatch/anyMatch/noneMatch
- AllMatch:所有元素匹配,返回true
- anyMatch:任何元素匹配,返回true
- noneMatch:任何元素都不匹配,返回true
/**
* allMatch使用: 操作,短路操作。所有元素匹配,返回true
*/
@Test
public void allMatchTest(){
boolean match = list.stream()
.allMatch(sku -> sku.getTotalPrice()>100);
System.out.println(match);
}
/**
* anyMatch使用:任何元素匹配,返回true
*/
@Test
public void anyMatchTest(){
boolean match = list.stream()
.anyMatch(sku -> sku.getTotalPrice()>100);
System.out.println(match);
}
/**
* noneMatch使用:任何元素都不匹配,返回true
*/
@Test
public void testNoneMatch(){
boolean match = list.stream()
.noneMatch(sku -> sku.getTotalPrice() System.out.println(sku.getSkuName()))
.findFirst();
System.out.println(JSON.toJSONString(optional.get(),true));
}
/**
* 找到任意一个
*/
@Test
public void findAnyTest(){
Optional optional = list.stream()
.peek(sku -> System.out.println(sku.getSkuName()))
.findAny();
System.out.println(JSON.toJSONString(optional.get(),true));
}
min/max/count
- min: 找出最小的项
- max:找出最大的项
- count:统计总数
@Test
public void maxTest(){
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.max();
System.out.println(optionalDouble.getAsDouble());
}
@Test
public void minTest(){
OptionalDouble optionalDouble = list.stream()
// 获取总价
.mapToDouble(Sku::getTotalPrice)
.min();
System.out.println(optionalDouble.getAsDouble());
}
/**
* count
*/
@Test
public void countTest(){
long count = list.stream().count();
System.out.println(count);
}
toList
将处理后的结果收集为list
// 收集总价格超过100的Sku
List cartSkuList = CartService.getCartSkuList();
List result = cartSkuList
.stream()
.filter(sku -> sku.getTotalPrice() > 100)
.collect(Collectors.toList());
System.out.println(JSON.toJSONString(result,true));
toMap
@Test
public void testMapTest(){
List cartSkuList = CartService.getCartSkuList();
//如果两个id重复,两个value可以映射到同一个id,如 678678重复
Map map = cartSkuList.stream().collect(Collectors.toMap(Sku::getSkuId, Sku::getSkuName,
(e1, e2) -> e1 + "," + e2));
System.out.println(map.get(678678));
System.out.println(JSON.toJSONString(map,true));
}
groupBy
@Test
public void groupTest(){
List cartSkuList = CartService.getCartSkuList();
Map group = cartSkuList
.stream()
.collect(Collectors.groupingBy(sku -> sku.getSkuCategory()));
System.out.println(JSON.toJSONString(group,true));
}