"背景 JDK 11 已经快要来了,所以 JDK 的新特征还是要及时学习的。 先从 JDK 8 的特征来入手吧。 本文先记录一下 lambda 表达式的学习。 另外,在 ClickHouse 的学习中,发现 ClickHouse 也支持了 lambda 表达式做为 SQL 的一部分。 所以 lambda 表达式已经是常识 .."

java8 lambda 表达式学习

本贴最后更新于 333 天前,其中的信息可能已经东海扬尘

背景

JDK 11 已经快要来了,所以 JDK 的新特征还是要及时学习的。

先从 JDK 8 的特征来入手吧。

本文先记录一下 lambda 表达式的学习。

另外,在 ClickHouse 的学习中,发现 ClickHouse 也支持了 lambda 表达式做为 SQL 的一部分。

所以 lambda 表达式已经是常识了。

lambda 表达式例子

创建线程

public class MainLambda {
	public static void main(String[] args) {
		Thread thread = new Thread(() -> System.out.println("Hello world!"));
		thread.start();
	}
}

输出

Hello world!

排序

public class MainLambda {
	public static void main(String[] args) {
		List<String> list = Arrays.asList(new String[] { "b", "c", "a" });
		Collections.sort(list, (s1, s2) -> s1.compareTo(s2));
		System.out.println(list);
	}
}

输出

[a, b, c]

转换成大写

public class MainLambda {
	public static void main(String[] args) {
		List<String> list = Arrays.asList(new String[] { "b", "c", "a" });
		List<String> newlist = list.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
		System.out.println(newlist);
	}
}

输出

[B, C, A]

遍历每个元素

import java.util.Arrays;
import java.util.List;

public class MainLambda {
	public static void main(String[] args) {
		List<String> list = Arrays.asList(new String[] { "b", "c", "a" });
		list.stream().forEach(s -> System.out.println(s.toUpperCase()));
	}
}

输出

B
C
A

lambda 表达式语法

一般语法

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

单参数语法

可以省略前面的小括号

param1 -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

单语句语法

可以省略后面的大括号, 以及 return 语句。

param1 -> statment

方法引用语法

Class or instance :: method

变量作用域

外部变量在 lambda 表达式引用时,jdk 8 编译器会隐式做为 final 来处理

stream

上面的转换成小写就是一个 stream 的例子。

执行概况

生成

collection.stream()

转换

汇聚 (Reduce)

collect 方法

示例

public class MainLambda {
	public static void main(String[] args) {
		List<Integer> nums = Lists.newArrayList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
		List<Integer> numsWithoutNull = nums.stream()
				.filter(num -> num != null)
				.collect(
						() -> new ArrayList<Integer>(),
						(list, item) -> list.add(item), 
						(list1, list2) -> list1.addAll(list2)
				);
		System.out.println(numsWithoutNull);
	}
}

结果

[1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

其中 collect 有三个参数

  1. supplier: 生成一个新的实例
  2. accumulator(对象,元素): 把元素加入对象中
  3. combiner(对象, 对象): 合并两个对象

此外,JDK 提供了 Collector 接口,方便来写 collect。这里不多写了。

另外,JDK 预定义了常用的 Collector 接口实现,如 Collectors.toList() 之类。

因此,上面的例子可以写成

public class MainLambda {
	public static void main(String[] args) {
		List<Integer> nums = Lists.newArrayList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
		List<Integer> numsWithoutNull = nums.stream()
				.filter(num -> num != null)
				.collect(Collectors.toList());
		System.out.println(numsWithoutNull);
	}
}

结果

[1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

reduce 方法

示例:求和元素

import com.google.common.collect.Lists;

public class MainLambda {
	public static void main(String[] args) {
		List<Integer> ints = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		int value = ints.stream()
				.reduce((sum, item) -> sum + item)
				.get();
		System.out.println("ints sum is:" + value);
	}
}

结果

ints sum is:55

其中 reduce 的两个参数,第一个参数 sum 是上一次 reduce 的返回值,第二个参数是本次的元素。所以实际执行过程是: ((( (1+2) + 3 )+ 4) + 5 )

也可以提供一个循环的初始值,如 -1

import java.util.List;

import com.google.common.collect.Lists;

public class MainLambda {
	public static void main(String[] args) {
		List<Integer> ints = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		int value = ints.stream()
				.reduce(-1, (sum, item) -> sum + item);
		System.out.println("ints sum is:" + value);
	}
}

结果

ints sum is:54

count

示例

import java.util.List;

import com.google.common.collect.Lists;

public class MainLambda {
	public static void main(String[] args) {
		List<Integer> ints = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		long value = ints.stream().count();
		System.out.println("ints sum is:" + value);
	}
}

结果

ints sum is:10

其它

执行示例

import java.util.List;

import com.google.common.collect.Lists;

public class MainLambda {
	public static void main(String[] args) {
		List<Integer> nums = Lists.newArrayList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
		int sum = nums.stream()
				.filter(num -> num != null)
				.distinct()
				.mapToInt(num -> num * 2)
				.skip(2)
				.limit(4)
				.peek(System.out::println)
				.sum();
		System.out.println("sum is:" + sum);
	}
}

结果: 首先 skip 掉了前 2 个元素,又 limit 了 4 个元素,所以最后剩下了 6,8,10,12 共 4 个元素,其和为 36。

6
8
10
12
sum is:36

并行执行

通过parallelStream,来实现并行执行,默认为 8 个并发。

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;

public class Java8AsyncRequest {
	public static void main(String[] args) {
		List<String> emitters = new ArrayList<String>();
		for (int i = 0; i != 1000; ++i) {
			emitters.add("http://www.abeffect.com/" + i);

		}

		emitters.parallelStream().map(new Function<String, Response>() {
			@Override
			public Response apply(String s) {
				try {
					System.out.println(Thread.currentThread() + ":" + s);
					Thread.sleep(5000L);
					return Request.Get(s).execute();
				} catch (IOException | InterruptedException e) {
					e.printStackTrace();
					return null;
				}
			}
		}).forEach(new Consumer<Response>() {

			@Override
			public void accept(Response response) {
				try {
					System.out.println(response.returnResponse().getStatusLine().getStatusCode());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
	}
}

参考

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:PipeSoloSymWide 等,欢迎大家加入,贡献开源。

    2232 引用 • 3683 回帖 • 616 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2168 引用 • 7424 回帖 • 1019 关注
感谢    关注    收藏    赞同    反对    举报    分享
15 回帖    
请输入回帖内容...
  • gitors      

    我不知道为什么,现在我同事还在拒绝 Java 8 ,我写的 简单的 list foreach, 我看到他看我代码的时候给我改成 for(){

    } 了。。。

    2 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • 88250            

    不学习会被社会抛弃的,只能祝他好运了。

       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • scmod            

    可能他用到了 index~? 滑稽 ~

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • gitors            

    是我写的代码啊。没用 index ,就是 遍历一下而已。

       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • gitors      

    今天叫我安装了代码统计插件,阶段性统计代码行数。。。难道是以代码行数评绩效的?我都不敢重构以前都代码了

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • flowaters            

    重构以前代码。。。这个还是很有挑战性的。

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • gitors            

    我所说的重构就是简单的修改,看哪里以前写的不好的,修改一下,要么是使代码简洁,要么是进行抽取封装,基本大的结构也是不敢改的

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • k1ncccc      

    😳

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • vinasis      

    半条命的 logo

       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • dkzwm      

    👍

       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • 111wsedfcgvb            

    啥表情哈哈

       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • Peiel            

    个人理解,无论大小,只要重构代码,就必须跑测试用例,否则很容易出问题。

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • gitors            

    单元测试不是打包必跑的吗?

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • TheNow            

    可以跳过啊 mvn package -Dmaven.test.skip=true 跳过单元测试.
    你的问题我理解的对吗?

    1 回复
       感谢    赞同    反对    举报    折叠    分享    评论    回复
  • gitors            

    是这样的,以前我们是持续集成,本地不跑单元测试,集成服务器还是会跑的,所以我们打包都不会跳过单元测试,否则可能导致集成失败

       感谢    赞同    反对    举报    折叠    分享    评论    回复
请输入回帖内容...