java8 两个list合并_java8新特性(上)

java8(又称 jdk1.8)中的新增功能是自 java1.0 发布 18 年以来,java 发生的最大变化,可以说是 java 发展史上的重大变革。java8 在老版本的基础上,没有去掉任何东西,所以 java8 支持向前兼容,你现有的代码仍然能正常工作。但是 java8 增加的几个新特性,能帮助你编写更清楚更简洁更高效的代码。

Java8 区别于以前的 Java 版本的一个重要特点就是函数式编程的风格。

那什么是函数式编程呢?

函数式编程属于编程范式中的一种,下面我们讲解一下命令式编程和函数式编程的区别,能够帮助我们更好地理解函数式编程。

命令式编程:命令式编程传递的是数据,将数据作为方法的参数或者返回值。

函数式编程:函数式编程传递的是函数("行为"或者"动作"),将函数作为方法的参数或者返回值,称之为行为参数化

需求:用程序计算数学表达式 (1 + 2) * 3 - 4 的结果。

  • 命令式编程:
// 程序1
public int calculate(){
   int a = 1 + 2;
   int b = a * 3;
   int c = b - 4;
   return c;
}

public static void main(String[] args){
   int result = calculate();
   System.out.println(result);
}
  • 函数式编程:
// 程序2
public static void main(String[] args){
   int result = subtract(multiply(add(1,2), 3), 4);
   System.out.println(result);
}

在程序 2 中,add 函数作为 multiply 函数的入参,multiply 函数作为 subtract 函数的入参,这就是函数式编程,函数可以作为参数。我们发现程序 1 需要三行代码,而程序 2 只需一行代码,所以相比于命令式编程,函数式编程会使代码更加简洁。

1、Lambda 表达式

(1)Lambda 表达式基本语法

Lambda 表达式可以取代大部分的匿名内部类,帮助我们写出更加简洁的代码,对照匿名内部类,讲解一下 Lambda 表达式的基本语法:

java8 引入一个新的操作符"->",该操作符称为箭头操作符或 Lambda 操作符。
">"的左侧:Lambda 表达式的参数列表,即匿名内部类接口中抽象方法的参数列表。
">"的右侧:Lambda 表达式所需执行的功能,称为 Lambda 体,即匿名内部类接口中抽象方法的具体实现。
以我的理解,Lambda 就是对匿名内部类做了升级,将匿名内部类中的核心代码抽取成一个表达式(即 Lambda 表达式),以此来减少不必要的重复代码。
  • 语法格式 1:若 Lambda 表达式参数列表中无参数,无返回值,用一个小括号表示参数列表。
() -> {System.out.ptintln("Hello World!");}
  • 语法格式 2:若 Lambda 表达式参数列表中有一个参数,参数小括号可以省略不写。
(x) -> System.out.ptintln(x);
x -> System.out.ptintln(x);
  • 语法格式 3:若 Lambda 表达式参数列表中有两个及两个以上参数,参数小括号必须写。
(x, y) -> System.out.ptintln(x + y);
  • 语法格式 4:若 Lambda 体只有一条语句,return 和大括号都可以省略不写。
(x, y) -> {return x + y;}
(x, y) -> x + y;
  • 语法格式 5:若 Lambda 体中有多条语句,多条语句必须用大括号括起来。
(int x, int y) -> {int z = x + y;return z;}
  • 语法格式 6:Lambda 表达式参数列表的数据类型可以省略不写,因为 JVM 编译器可以通过上下文推断推断出参数类型,这个过程我们称之为"类型推断"。如下所示我们讲解一下,JVM 如何做类型推断的。
// Lambda表达式
Comparator comparator = (x, y) -> Integer.compare(x, y);// Comparator接口源码@FunctionalInterfacepublic interface Comparator<T> { int compare(T o1, T o2);// 其他代码
}

JVM 根据 Comparator这句话判断泛型 T 为 Integer 类型,所以 compare 方法的两个参数都是 Integer 类型,最终判断出 Lambda 参数列表的 x 和 y 都是 Integer 类型。既然 JVM 自己可以判断出来,我们又何必多次一举写上呢。

(2) 使用 Lambda 表达式对接口的要求

虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,即只有一个抽象方法注意:这句话的意思不是说接口中只能有一个方法,这是因为 java8 的另一个新特性,接口中新增默认方法 default,被 default 修饰的方法会有默认实现,接口中有默认方法并不影响 Lambda 表达式的使用。上述所描述的接口称之为函数式接口

@FunctionalInterface 注解:用来修饰函数式接口的,要求接口中的抽象方法只有一个,如果被@FunctionalInterface 注解修饰的接口中有两个及以上抽象方法,编译会报错,这个注解往往会和 Lambda 表达式一起出现。如下 MyPredicate 接口是满足 Lambda 表达式对接口的要求的。

@FunctionalInterface
public interface MyPredicate<T> {
    // 接口中的方法,默认是public abstract,即抽象方法。
    boolean test(T t);

    // java8新增特性,接口中增加默认方法
    default String getName(){
        return "哈哈哈";
    }
}

(3)Lambda 表达式的优缺点

优点:使用 Lambda 表达式,可以帮助我们写出更加简洁紧凑的代码,减少代码量,减轻开发人员的工作量。

缺点:可读性差,如果是没有使用过java8的开发人员接收一个使用java8开发的项目,上手较慢。

(4)演示 Lambda 表达式如何使 java 代码更简洁

案例1(源码见 com.zxj.Lambda.package1.TestAnonymousInnerClassAndLambda.java)

需求:对 ArrayList 中的元素进行排序

实现:首先我们需要创建一个比较器(Comparator),然后调用 Collections 工具类的 sort 方法,使用比较器对 list 中的元素进行排序。

  1. 使用匿名内部类
// 使用匿名内部类对List进行排序
@Test
public void anonymousInnerClass(){
    // 使用匿名内部类创建一个比较器
    Comparator comparator = new Comparator() { @Overridepublic int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2);
        }
    };// 使用Collections工具类的sort方法对list中的元素进行排序
    Collections.sort(list, comparator);// 遍历list
    CommonUtil.printIntegerList(list);
}
  1. 使用Lambda表达式
@Test
public void lambda(){
    // 使用Lambda表达式创建一个比较器
    Comparator comparator = (x, y) -> Integer.compare(x, y);
    Collections.sort(list, comparator);
    CommonUtil.printIntegerList(list);
}

我们发现使用匿名内部类创建比较器,需要写一大堆代码,而使用 Lambda 表达式只需要一行代码,好处显而易见。

案例2(源码见 com.zxj.Lambda.TestLambda2.java)

需求:获取当前公司中,员工年龄大于 35 的员工信息。

  1. 使用 if 进行比较(源码见 com.zxj.Lambda.package2.useIf包)
// 根据年龄过滤的方法
public List filterAgeByIf(List employees){
    List emps = new ArrayList<>();for (Employee employee : employees){ if(employee.getAge() > 35){
            emps.add(employee);
        }
    }return emps;
}

当需求变为"获取公司中,工资大于 5000 的员工信息",比较的基准变了,此时我们需要重新写一个方法。

// 根据工资过滤的方法
public List filterSalaryByIf(List employees){
    List emps = new ArrayList<>();for (Employee employee : employees){ if(employee.getSalary() > 5000){
            emps.add(employee);
        }
    }return emps;
}

总结:使用 if 进行比较,一旦需求变了,就要增加一个方法,每个方法的核心是 if 判断语句,其他都是相同的代码,造成了代码重复。

  1. 使用策略模式(源码见 com.zxj.Lambda.package2.useStrategy 包) 创建策略接口
// 策略接口
public interface MyStrategy<T> {
    boolean filter(T t);
}

创建根据年龄过滤的具体策略类

// 根据年龄过滤的具体策略类
public class FilterEmployeeByAge implements MyStrategy<Employee> {
    @Override
    public boolean filter(Employee employee) {
        return employee.getAge() > 35;
    }
}

创建根据工资过滤的具体策略类

// 根据工资过滤的具体策略类
public class FilterEmployeeBySalary implements MyStrategy<Employee> {
    @Override
    public boolean filter(Employee employee) {
        return employee.getSalary() > 5000;
    }
}

根据具体策略对 employees 进行过滤

// 过滤方法,根据具体策略对employees进行过滤
public List filterByStategy(List employees, MyStrategy myStrategy){
   
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值