[TOC]
lambda和stream Api 都是Java8的新特性 首先 简单介绍一下java8
Java8 (jdk 1.8) 是Java语言开发的一个主要版本
Java8 是Oracle 公司于2014年3月发布,可以看成是自Java5以来最具革命性的版本。
Java8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
简介:
Lambda是一个匿名函数,我们可以把lambda表达式理解为是 一段可以传递的代码,即代码像数据一样进行传递。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格。
先来看一个简单案例:
需求:开启一个线程,在控制台输出 hello sky
下面分别使用三种方法实现
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("Hello sky !!!"); } }
//方式一 :实现runable接口 MyRunnable myRunable = new MyRunnable(); Thread t1 = new Thread(myRunable); t1.start();
//方式二 :匿名内部类 new Thread(new Runnable() { @Override public void run() { System.out.println("Hello sky !!!"); } }).start();
//方式三:lambda new Thread(() -> System.out.println("Hello sky !!!")).start();
在Java8语言中引入了一种新的语法元素和操作符 “->” ,该操作符被称为 Lambda 操作符 或 箭头操作符 。它将Lambda分为两个部分
左侧:指定了Lambda表达式需要的 参数列表
右侧:指定了 Lambda体 ,是抽象方法的实现逻辑,也即是Lambda表达式要执行的功能。
//语法格式一:无参,无返回值 Runnable r = () -> { System.out.println("Hello Lambda !"); }; //语法格式二:一个参数 没有返回值 Consumer<String> con1 = (String str) -> { System.out.println(str); }; //语法格式三:数据类型可以省略,可由编译器推断得出,称为 “类型推断” Consumer<String> con2 = (str) -> { System.out.println(str); }; //语法格式四:若只有一个参数,参数的小括号可以省略 Consumer<String> con3 = str -> { System.out.println(str); }; //语法格式五:多个参数 并有返回值 Comparator<Integer> com = (x,y) -> { System.out.println("两个参数,有返回值"); return Integer.compare(x,y); }; //语法格式六:当只有一条语句时,return和{} 都可以省略 Comparator<Integer> com2 = (x,y) -> Integer.compare(x,y);
类型推断:
上述Lambda表达式中的参数类型都是由编译器推断得出的。Lambda表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断得出了参数的类型,Lambda表达式的类型推断依赖于上下文环境,如上述语法格式三,就是根据Consumer中指定的泛型,可推断出参数类型为String.
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
@FunctionalInterface public interface MyInterface { int add(int a, int b); }
@FunctionalInterface 注解的作用只是检查这个接口是否为 函数式接口,并不是一定要加上这个注解
在idea中,如果接口不符合函数式接口的规范,编辑器会直接报错
在Java8中,接口中方法可以有默认实现,通过default关键字修饰的方法 就不是一个必须被实现的抽象方法,这种接口也是符合函数式接口规范的
/** * @Author sky * @Site cmtianxie163.com 2020/4/10 16:42 */ @FunctionalInterface public interface MyInterface { int add(int a, int b); default void test1(){} }
/** * @Author sky * @Site cmtianxie163.com 2020/4/10 22:00 */ public class LambdaTest4 { public static void main(String[] args) { happyMoney(100, m -> System.out.println("今天花了"+m)); List<String> list = Arrays.asList("北京", "上海", "南京", "六安", "合肥", "东京"); List<String> list1 = filterString2(list, s -> s.contains("京")); System.out.println(list1); } static void happyMoney(double money, Consumer<Double> con){ con.accept(money); } static List<String> filterString(List<String> list, Predicate<String> pre){ List<String> newlist = new ArrayList<>(); for (String s : list) { if (pre.test(s)){ newlist.add(s); } } return newlist; } static List<String> filterString2(List<String> list, Predicate<String> pre){ List<String> newlist = new ArrayList<>(); list.forEach(s -> {if (pre.test(s)){ newlist.add(s); }}); return newlist; } }
Java 四大内置核心函数式接口
函数式接口 | 参数类型 | 返回值类型 | 用途 |
---|---|---|---|
Consumer |
T | void | 对类型为T的对象应用操作,包含方法: void accept(T t) |
Supplier |
无 | T | 返回类型为T的对象,包含方法:T get() |
Function<T,R> 函数型接口 |
T | R | 对类型为T的对象应用操作,并返回结果。结 果是R类型的对象。包含方法:R apply(T t) |
Predicate |
T | boolean | 确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t) |
其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型为 T, U 参数应用操作,返回 R 类型的结 果。包含方法为: R apply(T t, U u); |
UnaryOperator |
T | T | 对类型为T的对象进行一元运算,并返回T类型的 结果。包含方法为:T apply(T t); |
BinaryOperator |
T,T | T | 对类型为T的对象进行二元运算,并返回T类型的 结果。包含方法为: T apply(T t1, T t2); |
BiConsumer<T,U> | T,U | void | 对类型为T, U 参数应用操作。 包含方法为: void accept(T t, U u) |
BiPredicate<T,U> | T,U | boolean | 包含方法为: boolean test(T t,U u) |
ToIntFunction |
T |
int long double |
分别计算int、long、double值的函数 |
IntFunction |
int long double |
R | 参数分别为int、long、double 类型的函数 |
先定义一个Employee类和EmployeeData类(提供假数据)
package org.itsky.study.test2; import com.sun.org.apache.xpath.internal.operations.Equals; import java.util.Objects; public class Employee { private int id; private String name; private int age; private double salary; public int getId() { return id; } public void setId(int id) { this.id = id; } 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 Employee() { System.out.println("Employee()....."); } public Employee(int id) { this.id = id; System.out.println("Employee(int id)....."); } public Employee(int id, String name) { this.id = id; this.name = name; } public Employee(int id, String name, int age, double salary) { this.id = id; this.name = name; this.age = age; this.salary = salary; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '/'' + ", age=" + age + ", salary=" + salary + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; if (id != employee.id) return false; if (age != employee.age) return false; if (Double.compare(employee.salary, salary) != 0) return false; return name != null ? name.equals(employee.name) : employee.name == null; } @Override public int hashCode() { int result; long temp; result = id; result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + age; temp = Double.doubleToLongBits(salary); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } }
public class EmployeeData { public static List<Employee> getEmployees(){ List<Employee> list = new ArrayList<>(); list.add(new Employee(1001, "鲁班七号", 34, 6000.38)); list.add(new Employee(1002, "黄忠", 12, 9876.12)); list.add(new Employee(1003, "孙尚香", 33, 3000.82)); list.add(new Employee(1004, "后羿", 26, 7657.37)); list.add(new Employee(1005, "成吉思汗", 65, 5555.32)); list.add(new Employee(1006, "狄仁杰", 42, 9500.43)); list.add(new Employee(1007, "伽罗", 26, 4333.32)); list.add(new Employee(1008, "马可波罗", 35, 2500.32)); list.add(new Employee(1008, "马可波罗", 35, 2500.32)); return list; } }
方法引用测试代码:
// 情况一:对象 :: 实例方法 //Consumer中的void accept(T t) //PrintStream中的void println(T t) public static void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing"); } //Supplier中的T get() //Employee中的String getName() public static void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); } // 情况二:类 :: 静态方法 //Comparator中的int compare(T t1,T t2) //Integer中的int compare(T t1,T t2) public static void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12,3)); } //Function中的R apply(T t) //Math中的Long round(Double d) public static void test4() { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply(Double d) { return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6)); } // 情况三:类 :: 实例方法 // Comparator中的int comapre(T t1,T t2) // String中的int t1.compareTo(t2) public static void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //BiPredicate中的boolean test(T t1, T t2); //String中的boolean t1.equals(t2) public static void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的R apply(T t) // Employee中的String getName(); public static void test7() { Employee employee = new Employee(1001, "Jerry", 23, 6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); }
格式: ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象 方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
例如:
Function<Integer,MyClass> fun = (n) → new MyClass(n);
等同于:
Function<Integer,MyClass> fun = MyClass::new;
格式:type[]::new
例如:
Function<Integer,Integer[]> fun = (n) → new Integer[n];
等同于:
Function<Integer,Integer[]> fun = (n) → Integer[]::new;
//构造器引用 //Supplier中的T get() //Employee的空参构造器:Employee() public static void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的R apply(T t) public static void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //BiFunction中的R apply(T t,U u) public static void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); } //数组引用 //Function中的R apply(T t) public static void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2)); }
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,Stream讲的是计算
一旦执行终止操作,就执行中间操作链,并产生结果。之后将不会再被使用
通过集合创建
/** * @Author sky * @Site cmtianxie163.com 2020/4/13 10:24 */ public class StreamAPITest1 { public static void main(String[] args) { List<String> list = Arrays.asList("java","python","go"); Stream<String> stream = list.stream(); Stream<String> parallelStream = list.parallelStream(); stream.forEach(s -> System.out.println(s)); System.out.println("----------------------"); parallelStream.forEach(s -> System.out.println(s)); } }
通过数组创建
Java8中的Arrays的静态方法 stream() 可以获取数组流
/** * Returns a sequential {@link Stream} with the specified array as its * source. * * @param <T> The type of the array elements * @param array The array, assumed to be unmodified during use * @return a {@code Stream} for the array * @since 1.8 */ public static <T> Stream<T> stream(T[] array) { return stream(array, 0, array.length); }
重载形式,能够处理对应基本类型的数组:
int[] array1 = new int[]{1,2,3,4,5}; IntStream intStream = Arrays.stream(array1); double[] array2 = new double[]{11,22,33,44}; DoubleStream doubleStream = Arrays.stream(array2); intStream.forEach(s -> System.out.println(s)); doubleStream.forEach(s -> System.out.prinln(s));
通过stream的 of()
可以调用Stream类静态方法of() ,通过显示值创建一个流,它可以接收任意数量的参数。
Stream<Object> objectStream = Stream.of("1", 1, 1.0, intStream); objectStream.forEach(s -> System.out.println(s));
可以使用静态方法Stream.iterate() 和 Stream.generate(),创建无限流。
迭代
public static Stream iterate(final T seed, final UnaryOperator f)
生成
public static Stream generate(Supplier s)
//创建无限流 //从10开始 遍历前十个偶数 Stream<Integer> iterateStream = Stream.iterate(0, t -> t + 2).limit(10); iterateStream.forEach(s -> System.out.println(s)); //生成 //生成十个随机数 Stream<Double> generateStream = Stream.generate(Math::random).limit(10); generateStream.forEach(System.out::println);
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作执行时一次性全部处理,称为 惰性求值
方法 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一 个空流。与 limit(n) 互补 |
List<Employee> list = EmployeeData.getEmployees(); //练习:查询员工表中薪资大于7000的员工信息 list.stream().filter(employee -> employee.getSalary()>7000).forEach(System.out::println); System.out.println("-------------------"); //截断流,使其元素不超过给定数量 list.stream().limit(3).forEach(System.out::println); System.out.println("-------------------"); //跳过元素,返回一个扔掉了前 n 个元素的流 list.stream().skip(3).forEach(System.out::println); System.out.println("-------------------"); //筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 list.stream().distinct().forEach(System.out::println);
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream。 |
mapToInt(ToTIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream。 |
flatMap(Function f) | 接收一个函数作为参数,将流中每个值都换成另一个流,然后把所有流连接成一个流 |
List<String> list1 = Arrays.asList("aa", "bb", "cc", "dd", "ee"); list1.stream().skip(1).map(str -> str.toUpperCase()).forEach(System.out::println); System.out.println("-------------------"); //获取员工姓名长度大于3的员工的姓名 list.stream().map(Employee::getName).filter(name -> name.length()>3).forEach(System.out::println); Stream<Stream<Character>> streamStream = list1.stream().map(StreamAPITest2::fromStringToStream); streamStream.forEach(System.out::println); System.out.println("-------------------"); //flatMap Stream<Character> characterStream = list1.stream().flatMap(StreamAPITest2::fromStringToStream); characterStream.forEach(System.out::println);
//将字符串中的多个字符构成的集合转换为对应的Stream实例 public static Stream<Character> fromStringToStream(String str){ ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()) { list.add(c); } return list.stream(); }
flatMap 有个类似的例子
如 list集合 如果想添加一个元素 这个元素本身也是集合
ArrayList list1 = new ArrayList(); list1.add(1); list1.add(2); list1.add(3); ArrayList list2 = new ArrayList(); list2.add(4); list2.add(5); list2.add(6); //集合长度加一 //list1.add(list2); //集合长度加三 list1.addAll(list2);
方法 | 描述 |
---|---|
sorted | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator c) | 产生一个新流,其中按比较器顺序排序 |
//自然排序 List<Integer> list2 = Arrays.asList(1,4,7,3,2,8,111,4); list2.stream().sorted().forEach(System.out::println); //定制排序 //安装年龄排序 年龄相同的再安装薪资排序 list.stream().sorted(((o1, o2) -> { int compare = Integer.compare(o1.getAge(), o2.getAge()); if(compare == 0){ return Double.compare(o1.getSalary(),o2.getSalary()); }else{ return compare; } })).forEach(System.out::println);
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代, 称为外部迭代。相反,Stream API 使用内部迭 代——它帮你把迭代做了) |
List<Employee> employees = EmployeeData.getEmployees(); //是否所有员工年龄都大于18 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch); //是否存在员工姓 孙 boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("孙")); System.out.println(noneMatch); //返回第一个元素 Optional<Employee> first = employees.stream().findFirst(); Employee employee = first.get(); System.out.println(employee); //返回当前流中的任意元素 Employee employee1 = employees.parallelStream().findAny().get(); System.out.println(employee1); //返回流中元素总个数 long count = employees.stream().count(); System.out.println(count); //返回最高工资 Stream<Double> doubleStream = employees.stream().map(e -> e.getSalary()); Double maxSalary = doubleStream.max(Double::compare).get(); System.out.println(maxSalary); //返回最低工资的员工 Employee minSalaryEmp = employees.stream().min((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary())).get(); System.out.println(minSalaryEmp); //内部迭代 employees.stream().forEach(System.out::println); //集合遍历 employees.forEach(System.out::println);
方法 | 描述 |
---|---|
reduce(T iden, BInaryOperator b) | 可以将流中的元素反复结合起来,得到一个值。返回T |
reduce(BinaryOperator b) |
可以将流中元素反复结合起来,得到一 个值。返回 Optional |
map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
//计算 1-10 的自然数之和 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer sum = list.stream().reduce(0, Integer::sum); System.out.println(sum); //计算公司所有员工工资的总和 Optional<Double> sumSalary = employees.stream().map(e -> e.getSalary()).reduce((s1, s2) -> s1 + s2); System.out.println(sumSalary.get());
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法 |
//练习1:查找工资大于6000的员工,结果返回为一个List或Set List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); employeeSet.forEach(System.out::println);
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、 Map)。 另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例, 具体方法与实例如下表:
方法 | 返回类型 | 作用 |
---|---|---|
toList |
List |
把流中元素收集到List |
toSet |
Set |
把流中元素收集到Set |
toCollection |
Collection |
把流中元素收集到创建的集合 |
counting | Long | 计算流中元素的个数 |
summingInt | Integer | 对流中元素的整数属性求和 |
averagingInt | Double | 计算流中元素Integer属性的平均值 |
summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。如:平 均值 |
joining | String | 连接流中每个字符串 |
maxBy |
Optional |
根据比较器选择最大值 |
minBy |
Optional |
根据比较器选择最小值 |
reducing | 归约产生的类型 | 从一个作为累加器的初始值开始, 利用BinaryOperator与流中元素逐 个结合,从而归约成单个值 |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果转 换函数 |
groupingBy |
Map<K,List |
根据某属性值对流分组,属性为K, 结果为V |
partitioningBy |
Map<Boolean,List |
根据true或false进行分区 |
List<Employee> emps= list.stream().collect(Collectors.toList()); Set<Employee> emps= list.stream().collect(Collectors.toSet()); Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new)); long count = list.stream().collect(Collectors.counting()); int total=list.stream().collect(Collectors.summingInt(Employee::getSalary)); double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary)); int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); String str= list.stream().map(Employee::getName).collect(Collectors.joining()); Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); Map<Emp.Status, List<Emp>> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus)); Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));