lambda表达式和Stream流是JDK8新增加的新特性,研究本文内容或者运行本文中的demo示例必须安装并使用JDK8以上的JDK版本。demo地址:https://gitee.com/huannzi/bigdataframework/tree/master/src/main/java/com/orkasgb/java
Lamdba表达式是JDK8加入的一种新特性,属于一种响应式编程体验,是一种关于函数定义,输入量,输出量的推演计算。简单的来说,Lambda就是函数式编程。
public interface DemoFactory {
Object getDemo();
}
/**
* 具体的实现类
*/
class DemoFactoryImpl implements DemoFactory{
@Override
public Object getDemo() {
return new Demo();
}
/**
* Lambda表达式作为参数
*
* @param factory 接口参数
* @return 实体
*/
public static Object getDemo(DemoFactory factory) {
return factory.getDemo();
}
/**
* Lambda表达式作为返回值
*
* @return 实体
*/
public static DemoFactory getDemo1() {
return () -> new Demo();
}
}
/**
* 实体类
*/
class Demo {
private String call = "Hello Word!";
public Demo() {
System.out.println(this.call);
}
}
/**
* 调用类
*/
class Main {
public static void main(String[] args) {
DemoFactory demo = () -> new Demo(); // 使用Lamdba表达式创建具体的实体对象
System.out.println(demo.getDemo());
}
}
/**
* 调用类
*/
class Main {
public static void main(String[] args) {
DemoFactoryImpl.getDemo(() -> new Demo()); // 使用Lamdba表达式作为参数
}
}
/**
* 调用类
*/
class Main {
public static void main(String[] args) {
DemoFactory factory = DemoFactoryImpl.getDemo1();// Lambda作为返回值
factory.getDemo();
}
}
函数式接口指的是只有一个抽象方法,且使用@FunctionalInterface注解注释的接口,称之为函数式接口。常见的函数式接口有以下几种:
@FunctionalInterface
public interface Runnable {
void run();
}
public class RunnableLambda {
public static void main(String[] args) {
new Thread(() -> System.out.println("RunnableLambda"), "thread1").start();
}
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
/**
* 函数式接口--Supplier
*/public class SupplierLambda {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
int max = getMax(() -> Arrays.stream(arr).max().getAsInt());
System.out.println("最大值:" + max);
}
private static int getMax(Supplier<Integer> supplier) {
return supplier.get();
}
}
@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (t) -> {
this.accept(t);
after.accept(t);
};
}
}
/**
* 函数式接口--Consumer
*/public class ConsumerLambda {
public static void main(String[] args) {
consumer(s -> System.out.println(s));
consumer(s -> System.out.println(s.toLowerCase()), s -> System.out.println(s.toUpperCase()));
}
/**
* Consumer中的抽象方法-accept,意思是消费一次
*
* @param consumer
*/
private static void consumer (Consumer<String> consumer) {
consumer.accept("Hello Word!");
}
/**
* Consumer中的默认方法-andThen,意思是多个消费者连接起来,各自消费一次
*
* @param first 第一个消费者
* @param second 第二个消费者
*/
private static void consumer (Consumer<String> first, Consumer<String> second) {
first.andThen(second).accept("Hello Word!");
}
}
/**
* 函数式接口-Comparator
*/public class ComparatorLambda {
public static void main(String[] args) {
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5);
/**
* sort()方法需要传入一个Comparator的实现,默认是按照自然排序。
*/
arr.sort((o1, o2) -> o2 - o1);
System.out.println(arr);
}
}
/**
* 函数式接口-Predicate
*/public class PredicateLambda {
public static void main(String[] args) {
addMethod(str -> str.toString().equals("HelloWord!"), str -> str.toString().equals("HelloWord!"));
notMethod(str -> !str.toString().equals("HelloWord!"));
orMethod(str -> !str.toString().equals("HelloWord!"), str -> str.toString().equals("HelloWord!"));
}
/**
* Predicate的and()方法,意思是两个判断都要满足条件
*
* @param one 第一个断言
* @param two 第二个断言
*/
private static void addMethod (Predicate one, Predicate two) {
String result = one.and(two).test("HelloWord!") ? "字符串符合要求!" : "字符串不符合要求!";
System.out.println("addMethod:" + result);
}
/**
* Predicate的not()方法,意思是当前判断结果取反
*
* @param one 第一个断言
*/
private static void notMethod (Predicate one) {
Predicate.not(one).test(true);
String result = String.valueOf(Predicate.not(one).test(true));;
System.out.println("notMethod:" + result);
}
/**
* Predicate的or()方法,意思是两个判断只要有一个满足条件就可以
*
* @param one 第一个断言
* @param two 第二个断言
*/
private static void orMethod (Predicate one, Predicate two) {
String result = one.or(two).test("HelloWord!") ? "字符串符合要求!" : "字符串不符合要求!";
System.out.println("orMethod:" + result);
}
}
/**
* 函数式接口-Function
*/public class FunctionLambda {
public static void main(String[] args) {
String result = getUpCase(str -> str.toUpperCase());
System.out.println("getUpCase:" + result);
String result1 = String.valueOf(andThenMethod(str -> str.concat("0"), str -> Integer.valueOf(str) * 20));
System.out.println("andThenMethod:" + result1);
}
/**
* apply("hello")的意思就是去处理"hello"这个字符串,然后返回处理的结果
*
* @param function 函数功能
* @return 返回处理的结果
*/
private static String getUpCase(Function<String, String> function) {
return function.apply("hello");
}
/**
* andThen()的意思就是one和two都去处理"10"这个字符串,但是one处理后的结果要作为two的入参,然后再返回two处理的结果
*
* @param one 第一个函数功能
* @param two 第二个函数功能
* @return 结果
*/
private static Integer andThenMethod(Function<String, String> one, Function<String, Integer> two) {
return one.andThen(two).apply("10");
}
}
1、可选的{},当Lambda表达式的函数体只有一句的时候,{}可以省略。
(String str) -> System.out.println(str);
2、可选的(),当Lambda表达式的参数只有一个的时候,()可以省略。
str -> System.out.println(str);
3、可选的return关键字,当Lambda表达式的函数体只有一句的时候,且返回值匹配的时候,可以省略return关键字。
(int a, int b) -> a + b;
4、可选的参数类型声明,Lambda表达式可以省略参数类型声明,因为参数动态进行推断。
(a, b) -> return a + b;
创建TestLambda.java文件,简单的编写一个循环打印数组的测试代码。
1、使用 javac TestLambda.java
编译TestLambda.java文件,将会生成TestLambda.class文件。
2、使用 javap -v -p TestLambda
反编译TestLambda.class文件。
3、使用 java -Djdk.internal.lambda.dumpProxyClasses TestLambda
执行TestLambda程序,并会生成TestLambda$$Lambda$1.class
文件,该文件是JAVA在运行时生成的由jvm生成的类,实际上就是由lambda表达式生成的内部类的具体实现。
// TestLambda.java
public class TestLambda {
static {
System.setProperty("jdk.internal.lambda.dumpProxyClasses", ".");
}
public static void main(String[] args) throws CloneNotSupportedException {
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5);
arr.forEach(i -> System.out.println(i));
}
}
// TestLambda.class,字节码文件中删除了部分内容,只保留了有用的地方
// class version 55.0 (55)
// access flags 0x21
public class TestLambda {
// access flags 0x9
public static main([Ljava/lang/String;)V throws java/lang/CloneNotSupportedException
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)V,
// handle kind 0x6 : INVOKESTATIC
TestLambda.lambda$main$0(Ljava/lang/Integer;)V,
(Ljava/lang/Integer;)V
]
INVOKEINTERFACE java/util/List.forEach (Ljava/util/function/Consumer;)V (itf)
// access flags 0x100A
private static synthetic lambda$main$0(Ljava/lang/Integer;)V
L0
LINENUMBER 12 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 0
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
RETURN
L1
LOCALVARIABLE i Ljava/lang/Integer; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 1
}
// TestLambda$$Lambda$1.class
final class TestLambda$$Lambda$1 implements Consumer {
private TestLambda$$Lambda$1() {
}
@Hidden
public void accept(Object var1) {
TestLambda.lambda$main$0((Integer)var1);
}
}
**总结:TestLambda.class中多出了一个private static synthetic lambda$main$0(Ljava/lang/Integer;)
方法,根据JAVA的特性,这是一个动态生成的函数,而在forEach参数里面,由原本的lambda表达式变成了由Consumer强转的具体的实现,由此说明,lambda就是一个匿名内部类对象,因为forEach接收的是一个Consumer,所以这里原本的lambda表达式变成了由Consumer强转的具体的实现。
ambda表达式变成了由Consumer强转的具体的实现中有一个TestLambda.lambda$main$0(Ljava/lang/Integer;)V
参数,而这个参数指向的就是TestLambda.class中多出来的那个private static synthetic lambda$main$0(Ljava/lang/Integer;)
。
TestLambda$$Lambda$1.class
是由JVM在运行时生成并通过特定参数保存下来的lambda表达式的具体实现的文件,可以看出,TestLambda$$Lambda$1
实现了Consumer接口并重写了accept方法。而该方法中刚好调用的就是TestLambda.lambda$main$0((Integer)var1);
。那么也就是说,JVM在运行的时候,将lambda表达式的方法体封装成一个具体的方法,如private static synthetic lambda$main$0(Ljava/lang/Integer;)
,而将lambda表达式变成了由对应接口强转的具体的实现,实现中将生成的该方法作为参数传入,如TestLambda.lambda$main$0(Ljava/lang/Integer;)V
,后续在生成具体实现的,在重写的接口方法中直接调用该该方法,如该例中,accept方法中直接就调用了传入的TestLambda.lambda$main$0
参数,进而完成lambda表达式的功能。
Stream流是专注于提供对容器的操作,比如遍历,过滤,统计等操作,提供了串行/并行两种模式,使用Fork/Join框架对任务任务拆分,已达到简化代码,提高编程效率与可读性的目的。
// 1、使用集合对象的stream()获取stream流对象
List<String> list = Arrays.asList("1", "2", "3", "4");
Stream<String> stream = list.stream();
System.out.println("使用集合对象的stream()获取stream流对象:" + stream);
// 2、使用stream流对象的静态方法of()、generate()、iterate()等方法获取获取stream流对象
Stream<List<String>> of = Stream.of(list);
System.out.println("使用stream流对象的静态方法of()方法获取获取stream流对象:" + of);
Stream<Integer> generate = Stream.generate(() -> 1 + 2);
System.out.println("使用stream流对象的静态方法generate()方法获取获取stream流对象:" + generate);
Stream<Integer> iterate = Stream.iterate(10, i -> i + 2);
System.out.println("使用stream流对象的静态方法iterate()方法获取获取stream流对象:" + iterate);
// 3、使用Arrays.stream()方法获取stream流对象
int[] arr = {1,2,3};
IntStream stream1 = Arrays.stream(arr);
System.out.println("使用Arrays.stream()方法获取stream流对象:" + stream1);
/**
* stream流对象的forEach方法
*/
@Test
public void testStreamAPIForEach(){
List<String> list = Arrays.asList("1", "2", "3", "4");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
}
/**
* stream流对象的count()
*/
@Test
public void testStreamAPICount(){
List<String> list = Arrays.asList("1", "2", "3", "4");
Stream<String> stream = list.stream();
long count = stream.count();
System.out.println(count);
}
@Test
public void testStreamAPIDistinct(){
List<String> list = Arrays.asList("1", "2", "2", "3", "3", "4", "4");
Stream<String> stream = list.stream();
Stream<String> distinct = stream.distinct();
distinct.forEach(System.out::println);
}
@Test
public void testStreamAPIMap(){
List<String> list = Arrays.asList("1", "2", "2", "3", "3", "4", "4");
Stream<String> stream = list.stream();
Stream<Integer> map = stream.map(str -> str.concat("0")).map(Integer::parseInt);
map.forEach(System.out::println);
Stream<String> stream1 = list.stream();
Stream<String> peek = stream1.peek(str -> str.concat("0")).peek(Integer::parseInt);
peek.forEach(System.out::println);
}
@Test
public void testStreamAPIFilter(){
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Stream<Integer> stream = list.stream();
Stream<Integer> filter = stream.filter(i -> i > 2);
filter.forEach(System.out::println);
Stream<Integer> stream1 = list.stream();
boolean anyMatch = stream1.anyMatch(i -> i == 2);
System.out.println("能匹配到一个2?:" + anyMatch);
Stream<Integer> stream2 = list.stream();
boolean allMatch = stream2.allMatch(i -> i == 2);
System.out.println("全部元素都是2?:" + allMatch);
Stream<Integer> stream3 = list.stream();
boolean noneMatch = stream3.noneMatch(i -> i == 2);
System.out.println("全部元素中没有一个是2?:" + noneMatch);
}
@Test
public void testStreamAPIReduce(){
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Stream<Integer> stream = list.stream();
// 不指定初始值,返回Optional对象
Integer reduce = stream.reduce(Integer::sum).get();
System.out.println("求和:" + reduce);
Stream<Integer> stream1 = list.stream();
// 指定初始值为0,当Stream流为空时就返回0,不为空就返回结果。
Integer reduce1 = stream1.reduce(0, Integer::sum);
System.out.println("求和:" + reduce1);
Stream<Integer> stream2 = list.stream();
// 指定初始值为0,并且将整个过程分成多个子流去处理,并且最后归并多个子流处理的结果(归并操作),当Stream流为空时就返回0,不为空就返回结果。
Integer reduce2 = stream2.reduce(0, (a, b) -> a + b, Integer::sum);
System.out.println("求和:" + reduce2);
Stream<String> stream3 = list.stream().map(i -> i.toString());
// 指定初始值为0,并且将整个过程分成多个子流去处理,并且最后归并多个子流处理的结果(归并操作),当Stream流为空时就返回0,不为空就返回结果。
String reduce3 = stream3.reduce("", (a, b) -> a.concat(b));
System.out.println("字符串拼接:" + reduce3);
Stream<String> stream4 = list.stream().map(i -> i.toString());
String reduce4 = stream4.reduce("", String::concat);
System.out.println("字符串拼接:" + reduce4);
}
@Test
public void testStreamAPISkipAndLimit(){
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Stream<Integer> stream = list.stream();
Stream<Integer> skip = stream.skip(2);
skip.forEach(System.out::println);
Stream<Integer> stream1 = list.stream();
Stream<Integer> limit = stream1.limit(1);
limit.forEach(System.out::println);
}
@Test
public void testStreamAPISkip(){
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Stream<Integer> stream = list.stream();
Stream<Integer> sorted = stream.sorted();
sorted.forEach(System.out::println);
Stream<Integer> stream1 = list.stream();
Stream<Integer> sorted1 = stream1.sorted((o1, o2) -> o2 - o1);
sorted1.forEach(System.out::println);
}
@Test
public void testStreamAPIMapTo(){
List<String> list = Arrays.asList("1", "2", "3", "4");
Stream<String> stream = list.stream();
IntStream mapToInt = stream.mapToInt(str -> Integer.parseInt(str.concat("0")));
mapToInt.forEach(System.out::println);
Stream<String> stream1 = list.stream();
DoubleStream mapToDouble = stream1.mapToDouble(str -> Double.parseDouble(str.concat("0")));
mapToDouble.forEach(System.out::println);
Stream<String> stream2 = list.stream();
LongStream mapToLong = stream2.mapToLong(str -> Long.parseLong(str.concat("0")));
mapToLong.forEach(System.out::println);
}
@Test
public void testStreamAPIFlatMapTo(){
List<String> list = Arrays.asList("1", "2", "3", "4");
Stream<String> stream = list.stream();
IntStream flatMapToInt = stream.flatMapToInt(str -> IntStream.of(Integer.parseInt(str.concat("0"))));
flatMapToInt.forEach(System.out::println);
Stream<String> stream1 = list.stream();
DoubleStream flatMapToDouble = stream1.flatMapToDouble(str -> DoubleStream.of(Double.parseDouble(str.concat("0"))));
flatMapToDouble.forEach(System.out::println);
Stream<String> stream2 = list.stream();
LongStream flatMapToLong = stream2.flatMapToLong(str -> LongStream.of(Long.parseLong(str.concat("0"))));
flatMapToLong.forEach(System.out::println);
}
@Test
public void testStreamAPICollect(){
List<String> list = Arrays.asList("1", "2", "3", "4", "4");
Stream<String> stream = list.stream();
List<String> collectToList = stream.collect(Collectors.toList());
collectToList.forEach(System.out::println);
Stream<String> stream1 = list.stream();
Set<String> collectToSet = stream1.collect(Collectors.toSet());
collectToSet.forEach(System.out::println);
Stream<String> stream2 = list.stream();
String collectJoining = stream2.collect(Collectors.joining("a"));
System.out.println(collectJoining);
Stream<String> stream3 = list.stream();
// 第一个参数:key的生成规则 第二个参数:value的生成规则 第三个参数(可选):当key重复时,value的处理规则
Map<Object, Object> collectToMap = stream3.collect(Collectors.toMap(str -> str + "a", str -> str, (o, n) -> n));
System.out.println(collectToMap);
Stream<String> stream4 = list.stream();
Map<Object, List<String>> collectGroupingBy = stream4.collect(Collectors.groupingBy(str -> str));
System.out.println(collectGroupingBy);
}
@Test
public void test(){
User user1 = new User("name1", 18, "dep1", "女");
User user2 = new User("name2", 15, "dep2", "男");
User user3 = new User("name3", 28, "dep2", "女");
User user4 = new User("name4", 30, "dep4", "男");
User user5 = new User("name5", 20, "dep1", "男");
User user6 = new User("name6", 20, "dep1", "女");
User user7 = new User("name6", 20, "dep1", "女");
User user8 = new User("name8", 20, "dep1", "男");
List<User> list = Arrays.asList(user1, user2, user3, user4, user5, user6, user7, user8);
Stream<User> stream = list.stream();
Map<String, List<User>> test = stream
.distinct() // 去重
.filter(user -> "女".equals(user.getSex())) // 过滤,性别为女的
.peek(user -> user.setSalary(2000 * 0.3 + 6000)) // 设置薪资属性的值
.sorted((u1, u2) -> u2.getAge() - u1.getAge()) // 按照年龄从小到大排序
.limit(5) // 分页,一次只取5条数据
.collect(Collectors.groupingBy(User::getDep)); // 按照部门分组
System.out.println(test);
}
/**
* 用户测试类
*/
@Data
class User {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 薪资
*/
private Double salary;
/**
* 部门
*/
private String dep;
/**
* 性别
*/
private String sex;
/**
* 有参构造函数
*
* @param name 姓名
* @param age 年龄
* @param dep 部门
* @param sex 性别
*/
public User(String name, int age, String dep, String sex) {
this.name = name;
this.age = age;
this.dep = dep;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", dep='" + dep + '\'' +
", sex='" + sex + '\'' +
", salary='" + salary + '\'' +
'}';
}
}
1、lambda表达式可以简化匿名内部类的写法,让编码更加便捷。
2、Stream流提供了一系列对容器或者集合API操作,提供了一系列的开箱即用的聚合操作函数。