java 函数式接口与Lambda表达式

java Lambda表达式

Lambda表达式 以下简写为 L表达式

1. 什么是 @FunctionalInterface 函数式接口

1.1 只含有且只有 单个抽象方法 的 接口

1.2 必须是接口 不能是抽象类

1.3 接口中可以有default (默认) 方法,私有方法和其他的静态方法

1.4 主要用途 用作 Lambda 表达式的类型

image.png

image.png

image.png

2. Lambda表达式为什么和接口中的方法名称没有关系

2.1 首先来说 L表达式 作为函数参数 一定是有类型的,那他是什么类型呢??

String     G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));

x -> x.toUpperCase() 这个表达式是什么类型的呢?

2.2 常见通用单参数函数

第一种 有入有出 类似一种 函数隐射(或转化工厂) 把输入T 转化为输出V

image.png

对应于

image.png

也就是说在你写出 L表达式 t-> {…; return r;} 这儿L 表达式的类型就是 Function

在你想把输入a转化为输入b时 就用的是Function 类型的L表达式

第二种类型 只进不出 也叫消费型,就是把你传入的T 给消费掉了

image.png

image.png

L表达式 t->{ 没有返回值} ,注意返回类型是void 也就是没有返回值

第三种类型 只出不进 也叫生产者型

image.png

L 表达式 ()->{…; return t;} ,注意输入参数为void

第四种类型 这是一种Function 变体

image.png

image.png

image.png

就是输入一个表达式 返回bolean 类型的
(t)->{…; return true or false };

Predicate atLeast5 = x - > x > 5;

Predicate 类型是一种特殊的 Function: Function<T, Boolean>

Predicate类型一般用于 filter 过滤数据

第五种类型:没进没出型

image.png

image.png

2.3 常用双参数函数式接口

BinaryOperator extends BiFunction<T,T,T>

image.png

BinaryOperator 一般用于 L表达式型如: (Integer a,Float b)->{ return a+b; }

BinaryOperator<Long> addLongs = (x, y) - > x + y;

由于L 是实现的 函数式接口,而函数式接口中只有单个抽象方法,所有没有方法名称
也能从上下文推断出来。另外函数的调用只和传入和传出的参数有关。

filter():对元素进行过滤; Predicate
sorted():对元素排序; function
map():元素的映射;function
distinct():去除重复元素;

3. lambada 表达式和 实现接口的关系

比如 线程的Runnable 接口

image.png


一般的代码:
 Runnable task=new Runnable() {
			@Override
			public void run() {
				System.out.println("努力工作中...");
			}
};Runnable task=()->{ System.out.println("努力工作中..."); }

Thead t=new Thead(task);
t.start();

也就是说
()->{ System.out.println("努力工作中..."); }Runnable 类型的实例


4. lambda 表达式的一般写法

image.png
image.png

image.png

5. 集合类的流式编程 【数据处理的工序】

先看一下整体
https://www.ibm.com/developerworks/cn/java/j-java-streams-3-brian-goetz/index.html

image.png

5.1 理解Map方法


以下代码把Integer List 隐射为String List
       List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4,3);
       List<String> distinct = numbers.stream().map( i ->  i+"" ).distinct().collect(Collectors.toList());
 
   map 中的 i->i+""  表达式就是 Function 类型的

   再看一个例子
      List<String>    G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
      String      G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));

** 理解flatMap**
map与flatMap都是用于转换已有的元素为其它元素,区别点在于:

map 必须是一对一的,即每个元素都只能转换为1个新的元素
flatMap 可以是一对多的,即每个元素都可以转换为1个或者多个新的元素
在这里插入图片描述

比如:有一个字符串ID列表,现在需要将其转为User对象列表。可以使用map来实现:

/**
 * 演示map的用途:一对一转换
 */
public void stringToIntMap() {
    List<String> ids = Arrays.asList("205", "105", "308", "469", "627", "193", "111");
    // 使用流操作
    List<User> results = ids.stream()
            .map(id -> {
                User user = new User();
                user.setId(id);
                return user;
            })
            .collect(Collectors.toList());
    System.out.println(results);
}

执行之后,会发现每一个元素都被转换为对应新的元素,但是前后总元素个数是一致的:

[User{id='205'}, 
 User{id='105'},
 User{id='308'}, 
 User{id='469'}, 
 User{id='627'}, 
 User{id='193'}, 
 User{id='111'}]

再比如:现有一个句子列表,需要将句子中每个单词都提取出来得到一个所有单词列表。这种情况用map就搞不定了,需要flatMap上场了:

public void stringToIntFlatmap() {
    List<String> sentences = Arrays.asList("hello world","Jia Gou Wu Dao");
    // 使用流操作
    List<String> results = sentences.stream()
            .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
            .collect(Collectors.toList());
    System.out.println(results);
}

执行结果如下,可以看到结果列表中元素个数是比原始列表元素个数要多的:

[hello, world, Jia, Gou, Wu, Dao]

这里需要补充一句,flatMap操作的时候其实是先每个元素处理并返回一个新的Stream,然后将多个Stream展开合并为了一个完整的新的Stream,如下:
在这里插入图片描述

5.2 理解 filter


List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());

     x -> x.length()> 2Predicate 的类型

其他的可以参考这里 http://www.importnew.com/16436.html

5.3 理解 分组 groupby 和收集器 collectors

1:把课程信息组装成map

   List<BwXyCourseinfo> courseList = courseinfoService.queryAll(pMap);
   Map<String, BwXyCourseinfo> courseMap = courseList.stream().collect(
				         Collectors.toMap(
						(BwXyCourseinfo o) -> o.getId(),
						(BwXyCourseinfo o) -> o,
						(k1, k2) -> {
				throw new RuntimeException(String.format("Duplicate key for values %s and %s", k1,k2));
						}, LinkedHashMap::new));2// 提取班号并去重复
    Set<String> bjmcSet = list.stream().map(o -> o.getClassid()).collect(Collectors.toSet());
		List<String> bjmcs = new ArrayList<String>(bjmcSet);
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("bjmcs", bjmcs);
		final Map<String, BwXyBjbm> bjMap = bjbmService.getBwXyBjbmMap(params);
		// 填充学院id
		list.stream().filter(o -> null != o.getClassid()).forEach(o -> {
			BwXyBjbm bj = bjMap.get(o.getClassid());
			if (null != bj) {
				o.setCollegeid(bj.getSsxy());
			}
		});

image.png

image.png

3: 字符串拼接
List names=new ArrayList<String>();
names.add("1");
names.add("2");
names.add("3");
System.out.println(String.join("-", names));
输出:
1-2-3

带前后缀
StringJoiner sj = new StringJoiner(",", "[", "]");

sj.add("a").add("b").add("c");

System.out.println("The final Joined string is " + sjr);
输出: "[a,b,c]

// 中间以or 连接,开始 and ( 结尾 )
category = new StringJoiner(" or ", " and ( ", " ) ");


例4:分组 ,这个比较费脑

List<ExamQuestionMain> mList = mainDao.queryAll(pMap);
// 按科目分组,英语,语文,数学,专业知识
Map<String, List<ExamQuestionMain>> subjectQuetionMap = mList.stream()
				.collect(
						Collectors.groupingBy(
								(ExamQuestionMain o) -> o.getSubject(),
								Collectors.toList()));


下边这个例子更费脑 
// 按问题id 分组 获取属性的 id列表
List<ExamQuestionAttribute> categoryList =null;
Map<String, List<String>> categoryMap = categoryList.stream().collect
(
Collectors.groupingBy(  
                        ExamQuestionAttribute::getQuestionId,
				   Collectors.mapping(ExamQuestionAttribute::getAttributeId, Collectors.toList())
                     )

);

// 一个 Question问题有多个Attribute
//在groupingBy 操作时 返回的类型为 Map<String, List<ExamQuestionAttribute>>{
     key  = QuestionId这个试题的id
     value= 这个试题对应的所有Attribute对象
   }

//但是 我想要 {key  =QuestionId这个试题的id , value=这个试题对应的所有 Attribute对象的id}
即返回结果 不是 Map<String, List<ExamQuestionAttribute>> 而是Map<String, List<String>>
所以还需要再次map一下也就是把 ExamQuestionAttribute 对象转化为一个String对象
Collectors.mapping(ExamQuestionAttribute::getAttributeId, Collectors.toList()
 

分组计数

image.png
多级分组

// 2级分组,先按子公司SubCompany 分组后再按Department 分组统计部门人数
public void groupByCompanyAndDepartment() {
    // 按照子公司+部门双层维度,统计各个部门内的人员数
    Map<String, Map<String, Long>> resultMap = getAllEmployees().stream()
            .collect(Collectors.groupingBy(Employee::getSubCompany,
                    Collectors.groupingBy(Employee::getDepartment,
                            Collectors.counting())));
    System.out.println(resultMap);
}

运行结果
{
    南京公司={
        测试二部=1, 
        测试一部=1}, 
    上海公司={
        研发二部=1, 
        研发一部=2}
}


6. 其他

Java 16 对此进行了改进,因为现在还可以在方法体中定义本地record ,
本地record 还可以在Collectors.groupingBy 多字段分组时使用

public List<Product> findProductsWithMostSaving(List<Product> products) {
  //方法内部的 record ,所谓的本地record 
  record ProductWithSaving(Product product, double savingInEur) {}

  products.stream()
    .map(p -> new ProductWithSaving(p, p.basePriceInEur * p.discountPercentage))
    .sorted((p1, p2) -> Double.compare(p2.savingInEur, p1.savingInEur))
    .map(ProductWithSaving::product)
    .limit(5)
    .collect(Collectors.toList());
}

更多例子请参考 :

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值