lambda是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁,更灵活的代码。作为一种更紧凑的代码风格,是java语言的表达能力得到了提升。
java8中引入了一个新的操作符 “->” 该操作符成为箭头操作符或lambda操作符
左侧:lambda 表达式的参数列表右侧:lambda 表达式所需执行的功能,即lambda体lambda表达式需要函数式接口的支持(见下图)若lambda体中的内容有方法已经实现了,我们可以使用"方法引用"(可以理解为方法引用是lambda表达式的另一种表现形式)
java8中有两个最为重要的改变,一个是lambda表达式;另一个则是 Stream API (java.util.stream.*) Stream 是 java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
注意:
Stream 自己不会储存元素。Stream 不会改变源对象。相反,他们会返回一个持有结果的新StreamStream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。一个数据源(如:集合,数组),获取一个流
示例
@Test public void test1(){ // 创建 Stream 流,可以通过 Collection 系列集合接口提供的 stream() 或 parallelStream() // 创建 Stream 流,可以通过 Arrays 的 stream 将数组转换成流 // 创建 Stream 流,可以通过 Stream.of() 方法获取流 // 创建流的第一种方式 List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); // 创建流的第二种方式 Employee[] emps = new Employee[10]; Stream<Employee> stream2 = Arrays.stream(emps); // 创建流的第三种方式 Stream<String> stream3 = Stream.of("aa", "bb", "cc"); // 创建流的第四种方式(创建无限流) // 1.迭代方式 Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2); stream4.limit(10).forEach(System.out::println); // 2.生成方式 Stream<Double> stream5 = Stream.generate(() -> Math.random()); stream5.limit(10).forEach(System.out::println); }一个中间操作链,对数据源的数据进行处理
(1)筛选与切片 filter 接收lambda,从流中排除某些元素。 limit 截断流,使其元素不超过给定数量。 skip 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 distinct 筛选,通过流所生成的元素的hashCode() 和 equals() 去除重复元素
filter List<Employee> employees = Arrays.asList( new Employee("张三",18, 9999.99), new Employee("李四",38, 5555.99), new Employee("王五",50, 6666.99), new Employee("赵六",16, 3333.99), new Employee("田七",8, 7777.99) ); @Test public void test1(){ Stream<Employee> employeeStream = employees.stream().filter((e) -> { System.out.println("Stream API 的中间操作"); return e.getAge() > 35; }); employeeStream.forEach(System.out::println); } limit // 返回流中前两个 Stream<Employee> employeeStream = employees.stream().limit(2); employeeStream.forEach(System.out::println); skip @Test public void test3(){ // 返回丢掉前两个元素的流 Stream<Employee> employeeStream = employees.stream().skip(2); employeeStream.forEach(System.out::println); } distinct @Test public void test4(){ // 这里去重是根据元素的 hashCode 和 equals 方法去除重复元素 Stream<Employee> employeeStream = employees.stream().distinct(); employeeStream.forEach(System.out::println); }(2)映射 map-接收 lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个 flatMap-接收一个函数作为参数,将流中的每个值都替换成另一个流,然后把所有流连接成一个流。
示例1:元素转换成其他形式
@Test public void test5(){ List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee"); // 这里会返回一个新流 Stream<String> stream = list.stream().map((str) -> str.toUpperCase()); stream.forEach(System.out::println); }示例2:提取流中的元素
@Test public void test6(){ // 这里会返回一个新流 Stream<String> stream = employees.stream().map(Employee::getName); stream.forEach(System.out::println); }(3)排序 sorted() 自然排序 sorted(Comparator com) 定制排序
示例1:自然排序
@Test public void test7(){ List<String> list = Arrays.asList("ccc", "aaa", "bbb", "ddd", "eee"); list.stream().sorted().forEach(System.out::println); }示例2:定制排序
@Test public void test8(){ Stream<Employee> stream = employees.stream().sorted((x, y) -> (int) (x.getSalary() - y.getSalary())); stream.forEach(System.out::println); }一个终止操作,执行中间操作链,并产生结果
Employee.java
package com.miracle.java8.test1; import java.util.Objects; public class Employee { private String name; private int age; private double salary; private Status status; public Employee() { } public Employee(String name, int age, double salary, Status status) { this.name = name; this.age = age; this.salary = salary; this.status = status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return age == employee.age && Double.compare(employee.salary, salary) == 0 && Objects.equals(name, employee.name); } @Override public int hashCode() { return Objects.hash(name, age, salary); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", salary=" + salary + ", status=" + status + '}'; } public enum Status{ FREE, BUSY, VOCATION } }(1)查找与匹配
allMatch 检查是否匹配所有元素 @Test public void test9(){ boolean b = employees.stream().allMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b); } anyMatch 检查是否至少匹配一个元素 @Test public void test10(){ boolean b = employees.stream().anyMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b); } noneMatch 检查是否没有匹配所有元素 @Test public void test11(){ boolean b = employees.stream().noneMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b); } findFirst 返回第一个元素findAny 返回当前流中的任意元素count 返回流中元素的总个数max 返回流中最大值min 返回流中最小值(2)规约 reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值
实例一:将集合中元素求和
@Test public void test12(){ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 这里reduce把集合里面数据相加,起始值为0 Integer sum = list.stream().reduce(0, (x, y) -> x + y); System.out.println(sum); }Stream流操作默认产生的是串行流(单线程,速度慢),Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流直接进行转换
串行流示例
@Test public void test13(){ // 产生一个long型数字组成的流,是串行流(单线程,速度慢) LongStream longStream = LongStream.rangeClosed(0, 100000000000L); // 进行加和操作 long result = longStream.reduce(0, Long::sum); System.out.println(result); }并发流示例
@Test public void test14(){ // 产生一个long型数字组成的流,是并发流(多线程,速度快) LongStream longStream = LongStream.rangeClosed(0, 100000000000L).parallel(); // 进行加和操作 long result = longStream.reduce(0, Long::sum); System.out.println(result); }Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
LocalDate,LocalTime,LocalDateTime类的实例是不可变的对象(线程安全),分别表示使用ISO-8601日历系统的日期,时间,日期和时间,三者API相同用法一致,下面以 LocalDateTime 为例
@Test public void test1(){ // 获取当前系统时间 LocalDateTime now = LocalDateTime.now(); System.out.println(now); // 获取年 int year = now.getYear(); System.out.println(year); // 获取月 int month = now.getMonthValue(); System.out.println(month); // 获取天 int day = now.getDayOfMonth(); System.out.println(day); // 时 int hour = now.getHour(); System.out.println(hour); // 构造时间 LocalDateTime localDateTime = LocalDateTime.of(2015, 10, 19, 13, 22, 33); System.out.println(localDateTime); // 日期运算 LocalDateTime nowTime = LocalDateTime.now(); // 时间相加(plus系列方法,年月日时分秒),加两年 LocalDateTime plusYears = nowTime.plusYears(2); System.out.println(plusYears); // 时间相减(minus系列方法,年月日时分秒) 减两..年 LocalDateTime minusYears = plusYears.minusYears(2); System.out.println(minusYears); }