当前位置:   article > 正文

JDK8新特性-个人笔记

jdk8新特性

视频地址:视频去哪了呢?_哔哩哔哩_bilibili

目录

一、Lambda表达式

二、Lambda需要“函数式接口”的支持

三、Java8  内置的核心函数式接口

四、方法引用与构造器引用

五、Stream API

六、并行流与顺序流

七、Optional类

八、接口中的默认方法与静态方法

九、新时间日期API

十、注解相关


一、Lambda表达式

 Lambda 表达式也可称为闭包,是推动 Java 8 发布的最重要新特性。lambda表达式本质上是一个匿名方法。Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)或者把代码看成数据。使用 Lambda 表达式可以使代码变的更加简洁紧凑。在最简单的形式中,一个lambda可以由:用逗号分隔的参数列表–>符号函数体三部分表示,在某些情况下lambda的函数体会更加复杂,这时可以把函数体放到在一对花括号中,就像在Java中定义普通函数一样。Lambda可以引用类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转为final,这样效率更高)。Lambda可能会返回一个值。返回值的类型也是由编译器推测出来的。如果lambda的函数体只有一行的话,那么没有必要显式使用return语句。

    如何使现有的函数友好地支持lambda。最终采取的方法是:增加函数式接口的概念。函数式接口就是接口里面必须有且只有一个抽象方法的普通接口,像这样的接口可以被隐式转换为lambda表达式成为函数式接口。 在可以使用lambda表达式的地方,方法声明时必须包含一个函数式的接口。 任何函数式接口都可以使用lambda表达式替换,例如:ActionListener、Comparator、Runnable。

    函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java 8增加了一种特殊的注解@FunctionalInterface,但是默认方法与静态方法并不影响函数式接口的契约,可以任意使用。

    使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。通过() -> {}代码块替代了整个匿名类。

  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. import org.junit.Test;
  3. import org.springframework.boot.test.context.SpringBootTest;
  4. import java.util.*;
  5. /**
  6. * @author WangTianShun
  7. * @date 2020/10/21 14:11
  8. */
  9. @SpringBootTest
  10. public class TestLambda {
  11. //原来的匿名内部类
  12. @Test
  13. public void Test(){
  14. Comparator<Integer> comparator = new Comparator<Integer>() {
  15. @Override
  16. public int compare(Integer o1, Integer o2) {
  17. return Integer.compare(o1,o2);
  18. }
  19. };
  20. TreeSet<Integer> ts = new TreeSet<>(comparator);
  21. }
  22. //Lambda表达式
  23. @Test
  24. public void test2(){
  25. Comparator<Integer> comparator =(x,y) -> Integer.compare(x,y);
  26. TreeSet<Integer> ts = new TreeSet<>(comparator);
  27. }
  28. /*============================================================*/
  29. List<Employee> employees = Arrays.asList(
  30. new Employee("wts1",21,22222.2),
  31. new Employee("wts2",22,32222.2),
  32. new Employee("wts3",23,42222.2),
  33. new Employee("wts4",24,52222.2)
  34. );
  35. //需求:获取当前公司中员工年龄大于35的员工信息
  36. @Test
  37. public void test3(){
  38. List<Employee> employees = filterEmployee1(this.employees);
  39. for (Employee emp : employees) {
  40. System.out.println(emp);
  41. }
  42. }
  43. public List<Employee> filterEmployee1(List<Employee> list){
  44. List<Employee> emps = new ArrayList<>();
  45. for (Employee emp : emps){
  46. if (emp.getAge() >= 22){
  47. emps.add(emp);
  48. }
  49. }
  50. return emps;
  51. }
  52. //需求:获取当前公司中员工年工资大于5000
  53. public List<Employee> filterEmployee2(List<Employee> list){
  54. List<Employee> emps = new ArrayList<>();
  55. for (Employee emp : emps){
  56. if (emp.getSalary()<5000){
  57. emps.add(emp);
  58. }
  59. }
  60. return emps;
  61. }
  62. //优化方式一:策略设计模式
  63. @Test
  64. public void test4(){
  65. List<Employee> employees = filterEmployee3(this.employees, new FilterEmployeeByAge());
  66. for (Employee employee : employees) {
  67. System.out.println(employee);
  68. }
  69. List<Employee> employees1 = filterEmployee3(this.employees, new FilterEmployeeBySalary());
  70. for (Employee employee1:employees1) {
  71. System.out.println(employee1);
  72. }
  73. }
  74. public List<Employee> filterEmployee3(List<Employee> employees, MyPredicate<Employee> mp){
  75. List<Employee> emps = new ArrayList<>();
  76. for (Employee employee : employees) {
  77. if (mp.test(employee)){
  78. emps.add(employee);
  79. }
  80. }
  81. return emps;
  82. }
  83. //优化方式二:匿名内部类
  84. @Test
  85. public void test5(){
  86. List<Employee> employees = filterEmployee3(this.employees, new MyPredicate<Employee>() {
  87. @Override
  88. public boolean test(Employee employee) {
  89. return employee.getSalary() > 3000;
  90. }
  91. });
  92. for (Employee employee: employees) {
  93. System.out.println(employee);
  94. }
  95. }
  96. //优化方式三:Lambda表达式
  97. @Test
  98. public void test6(){
  99. List<Employee> list = filterEmployee3(employees,(e) -> e.getSalary() > 3000);
  100. list.forEach(System.out::println);
  101. }
  102. //优化方式四:Stream API
  103. @Test
  104. public void test7(){
  105. employees.stream().filter((e) -> e.getSalary() >3000)
  106. .limit(2)
  107. .forEach(System.out::println);
  108. System.out.println("-----------------------------------------");
  109. employees.stream().map(Employee::getName)
  110. .forEach(System.out::println);
  111. }
  112. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import lombok.ToString;
  6. import org.junit.Test;
  7. /**
  8. * @author WangTianShun
  9. * @date 2020/10/21 14:22
  10. */
  11. @Data
  12. @AllArgsConstructor
  13. @NoArgsConstructor
  14. @ToString
  15. public class Employee {
  16. private String name;
  17. private Integer age;
  18. private double salary;
  19. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. /**
  3. * @author WangTianShun
  4. * @date 2020/10/21 14:34
  5. */
  6. public interface MyPredicate<T> {
  7. public boolean test(T t);
  8. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. /**
  3. * @author WangTianShun
  4. * @date 2020/10/21 14:35
  5. */
  6. public class FilterEmployeeByAge implements MyPredicate<Employee> {
  7. @Override
  8. public boolean test(Employee employee) {
  9. return employee.getAge() > 35;
  10. }
  11. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. /**
  3. * @author WangTianShun
  4. * @date 2020/10/21 14:43
  5. */
  6. public class FilterEmployeeBySalary implements MyPredicate<Employee> {
  7. @Override
  8. public boolean test(Employee employee) {
  9. return employee.getSalary() >3000;
  10. }
  11. }

Lambda 表达式的基础语法:

        Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或Lambda 操作符
         箭头操作符将Lambda表达式拆分成两部分
         左侧:Lambda表达式的参数列表
         右侧:Lambda表达式中所需要执行的功能  即Lambda体

         语法格式一:无参数,无返回值
                  () -> System.out.println("hello");

         语法格式二:有一个参数,并且无返回值
                  (x) -> System.out.println(x);

         语法格式三:若只有一个参数,小括号可以省略不写
                  x -> System.out.println(x);

         语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句
                  Comparator<Integer> com = (x, y) ->{
                     System.out.println("函数式接口");
                     return Integer.compare(x,y);
                 };

          语法格式五:若Lambda体中只有一条语句,return 和大括号就可以省略不写
                  Comparator<Integer> com = (x, y) -> Integer.compare(x,y);

          语法格式六:Lambda 表达式的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即"类型推断"
                  Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x,y);
          左右遇一括号省
          左侧推断类型省

二、Lambda需要“函数式接口”的支持

函数式接口:有且只有一个抽象方法的接口,称之为函数式接口。当然接口中可以包含其他的方法(默认、静态,私有)

                      使用@FunctionalInterface注解修饰

                      作用:可以检测接口是否是一个函数式接口 是:编译成功 否:编译失败(接口中没有抽象方法,接口中的抽象方法个数多余1个)

  1. @SpringBootTest
  2. public class TestLambda2 {
  3. @Test
  4. public void run(){
  5. int num = 0; //jdk1.7前,必须是 final
  6. Runnable r = new Runnable(){
  7. @Override
  8. public void run() {
  9. System.out.println("hello word");
  10. }
  11. };
  12. r.run();
  13. System.out.println("-------------------------");
  14. Runnable r1 = () -> System.out.println("Hello Word");
  15. r1.run();
  16. }
  17. @Test
  18. public void test2(){
  19. Consumer<String> con = x -> System.out.println(x);
  20. con.accept("hello");
  21. }
  22. @Test
  23. public void test3(){
  24. Comparator<Integer> com = (x, y) ->{
  25. System.out.println("函数式接口");
  26. return Integer.compare(x,y);
  27. };
  28. }
  29. @Test
  30. public void test4(){
  31. Comparator<Integer> com = ( x, y) -> Integer.compare(x,y);
  32. }
  33. //需求:对一个数进行运算
  34. @Test
  35. public void test5(){
  36. Integer num = operation(100,(x) -> x*x);
  37. System.out.println(num);
  38. System.out.println(operation(200, (y) -> y + 200));
  39. }
  40. public Integer operation(Integer num, MyFunction mf){
  41. return mf.getValue(num);
  42. }
  43. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. import org.junit.Test;
  3. import org.springframework.boot.test.context.SpringBootTest;
  4. import java.util.Arrays;
  5. import java.util.Collections;
  6. import java.util.List;
  7. /**
  8. * @author WangTianShun
  9. * @date 2020/10/21 16:18
  10. */
  11. @SpringBootTest
  12. public class TestLambda3 {
  13. List<Employee> employees = Arrays.asList(
  14. new Employee("wts2",22,22222.2),
  15. new Employee("wts1",22,32222.2),
  16. new Employee("wts3",23,42222.2),
  17. new Employee("wts4",24,52222.2)
  18. );
  19. @Test
  20. public void test1(){
  21. Collections.sort(employees, (e1,e2) ->{
  22. if (e1.getAge() == e2.getAge()){
  23. return e1.getName().compareTo(e2.getName());
  24. }else {
  25. return Integer.compare(e1.getAge(), e2.getAge());
  26. }
  27. });
  28. for (Employee employee : employees){
  29. System.out.println(employee);
  30. }
  31. }
  32. @Test
  33. public void test02(){
  34. String trimStr = strHandler("\t\t\t 我大尚硅谷威武 ", (str)-> str.trim()); //str.trim()去掉字符串首尾的空格
  35. System.out.println(trimStr);
  36. String upperCaseStr = strHandler("abcdef",(str) -> str.toUpperCase());
  37. System.out.println(upperCaseStr);
  38. }
  39. //需求:用于处理字符串的方法
  40. public String strHandler(String str, MyFunction01 mf){
  41. return mf.getValue(str);
  42. }
  43. @Test
  44. public void test3(){
  45. op(100L,200L, (x,y) -> x+y);
  46. }
  47. //需求:对于两个Long 型数据进行处理
  48. public void op(Long l1, Long l2, MyFunction02<Long,Long> mf){
  49. System.out.println(mf.getValue(l1,l2));
  50. }
  51. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. /**
  3. * @author WangTianShun
  4. * @date 2020/10/21 20:28
  5. */
  6. public interface MyFunction01 {
  7. public String getValue(String str);
  8. }
  1. package com.wts.mybatisplus.jdk8.Lambda;
  2. /**
  3. * @author WangTianShun
  4. * @date 2020/10/21 20:40
  5. */
  6. public interface MyFunction02<T,R> {
  7. public R getValue(T t1, T t2);
  8. }

三、Java8  内置的核心函数式接口
 

  1. @SpringBootTest
  2. public class TestLambda4 {
  3. //Predicate<T> : 断言型接口
  4. @Test
  5. public void test4(){
  6. List<String> list = Arrays.asList("hello","atguigu","Lambda","www","ok0");
  7. List<String> strings = filterStr(list, (s) -> s.length() > 3);
  8. for (String str : strings) {
  9. System.out.println(str);
  10. }
  11. }
  12. //需求:将满足条件的字符串放到集合当中
  13. public List<String> filterStr(List<String> list, Predicate<String> pre){
  14. List<String> strlist = new ArrayList<>();
  15. for (String str: strlist) {
  16. if (pre.test(str)){
  17. strlist.add(str);
  18. }
  19. }
  20. }
  21. @Test
  22. public void test3(){
  23. String newStr = strHandler("\t\t\t 我是哈哈哈 ", (str) -> str.trim());
  24. System.out.println(newStr);
  25. }
  26. //Function<T,R> :函数型接口
  27. //需求:用于处理字符串
  28. public String strHandler(String str, Function<String, String> fun){
  29. return fun.apply(str);
  30. }
  31. //Supplier<T>:供给型接口
  32. @Test
  33. public void test02(){
  34. getNumList(10,() -> (int)(Math.random() * 100));
  35. }
  36. //需求:产生指定个数的整数,并放入集合中
  37. public List<Integer> getNumList(int num, Supplier<Integer> sup){
  38. List<Integer> list = new ArrayList<>();
  39. for (int i = 0; i < num; i++) {
  40. Integer e = sup.get();
  41. list.add(e);
  42. }
  43. return list;
  44. }
  45. //Consumer<T> 消费型接口
  46. @Test
  47. public void test01(){
  48. happy(10000, (m) -> System.out.println("大哥们每次消费:"+ m +"元"));
  49. }
  50. public void happy(double money, Consumer<Double> con){
  51. con.accept(money);
  52. }
  53. }

四、方法引用与构造器引用

方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。 

* 一、方法引用:若Lambda 体中的内容有方法已经实现了,我们可以使用"方法引用"
*      (可以理解为方法引用是Lambda 表达式的另一种表现形式)
*
* 主要有三种语法格式
*
*  对象::实例方法名
*
*  类::静态方法名
*
*  类::实例方法名
*  Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口
*  Lambda 参数列表中的第一个参数是 实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
*
*  二、构造器引用:
*
*  格式:
*  ClassName::new
*
*  注意:需要调研的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
*
*  三、数组数组引用
*
*  type::new;
 

定义了4个方法的Car这个类作为例子,区分Java中支持的4种不同的方法引用。

public static class Car {

    public static Car create( final Supplier< Car > supplier ) {

        return supplier.get();

    }                       

    public static void collide( final Car car ) {

        System.out.println( "Collided " + car.toString() );

    }         

    public void follow( final Car another ) {

        System.out.println( "Following the " + another.toString() );

    }         

    public void repair() {  

        System.out.println( "Repaired " + this.toString() );

    }

}

第一种方法引用是构造器引用,它的语法是Class::new,或者更一般的Class< T >::new。请注意构造器没有参数。

    final Car car = Car.create( Car::new );

    final List< Car > cars = Arrays.asList( car ); 

第二种方法引用是静态方法引用,它的语法是Class::static_method。请注意这个方法接受一个Car类型的参数

    cars.forEach( Car::collide );

第三种方法引用是特定类的任意对象的方法引用,它的语法是Class::method。请注意,这个方法没有参数。

    cars.forEach( Car::repair );

第四种方法引用是特定对象的方法引用,它的语法是instance::method。请注意,这个方法接受一个Car类型的参数

    final Car police = Car.create( Car::new );

    cars.forEach( police::follow );

  1. public class TestMethodRef {
  2. //1、对象::实例方法名
  3. @Test
  4. public void test01(){
  5. Consumer<String> con = (x) -> System.out.println(x);
  6. PrintStream ps = System.out;
  7. Consumer<String> con1 = ps::println;
  8. Consumer<String> con2 = System.out::println;
  9. con2.accept("abcdef");
  10. }
  11. @Test
  12. public void test02(){
  13. Employee employee =new Employee();
  14. Supplier<String> sup = () -> employee.getName();
  15. String str = sup.get();
  16. System.out.println(str);
  17. Supplier<Integer> sup2 = employee::getAge;
  18. Integer num = sup2.get();
  19. System.out.println(num);
  20. }
  21. //2、类::静态方法名
  22. @Test
  23. public void test03(){
  24. Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
  25. Comparator<Integer> com1 = Integer::compare;
  26. }
  27. //类:静态方法名
  28. public void test04(){
  29. BiPredicate<String,String> bp = (x,y) -> x.equals(y);
  30. }
  31. @Test
  32. public void test05(){
  33. Supplier<Employee> sup = () -> new Employee();
  34. sup.get();
  35. //构造器引用方式
  36. Supplier<Employee> sup2 = Employee::new;
  37. Employee employee = sup2.get();
  38. System.out.println(employee);
  39. }
  40. @Test
  41. public void test06(){
  42. Function<Integer,Employee> fun2 = Employee::new;
  43. Employee employee = fun2.apply(23);
  44. System.out.println(employee);
  45. }
  46. //数组引用
  47. public void test07(){
  48. Function<Integer,String[]> fun = (x) -> new String[x];
  49. String[] strs = fun.apply(10);
  50. System.out.println(strs.length);
  51. Function<Integer,String[]> fun2 = String[]::new;
  52. String[] strs2 = fun2.apply(20);
  53. System.out.println(strs2.length);
  54. }
  55. }

五、Stream API

Java 8 API添加了一个新的抽象称为流Stream把真正的函数式编程风格引入到Java中,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API极大简化了集合框架的处理,这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

Stream流有一些特性:

    1.Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。

    2.这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。

    3.Stream不保存数据,故每个Stream流只能使用一次。

    所以这边有两个概念:流、管道。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。这里有2个操作:中间操作、最终操作。

    中间操作:返回结果都是Stream,故可以多个中间操作叠加。

    终止操作:用于返回我们最终需要的数据,只能有一个终止操作。

    使用Stream流,可以清楚地知道我们要对一个数据集做何种操作,可读性强。而且可以很轻松地获取并行化Stream流,不用自己编写多线程代码,可以更加专注于业务逻辑。默认情况下,从有序集合、生成器、迭代器产生的流或者通过调用Stream.sorted产生的流都是有序流,有序流在并行处理时会在处理完成之后恢复原顺序。无限流的存在,侧面说明了流是惰性的,即每当用到一个元素时,才会在这个元素上执行这一系列操作。

Stream的三个操作步骤

    1.创建Stream

    2.转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换)

    3.对Stream进行聚合操作,获取想要的结果

一、 流的生成方法

    1.Collection接口的stream()或parallelStream()方法

    2.静态的Stream.of()、Stream.empty()方法

    3.Arrays.stream(array, from, to)

    4.静态的Stream.generate()方法生成无限流,接受一个不包含引元的函数

    5.静态的Stream.iterate()方法生成无限流,接受一个种子值以及一个迭代函数

    6.Pattern接口的splitAsStream(input)方法

    7.静态的Files.lines(path)、Files.lines(path, charSet)方法

    8.静态的Stream.concat()方法将两个流连接起来

二、 流的Intermediate方法(中间操作)

    1.filter(Predicate) :将结果为false的元素过滤掉

    2.map(fun) :转换元素的值,可以用方法引元或者lambda表达式

    3.flatMap(fun) :若元素是流,将流摊平为正常元素,再进行元素转换

    4.limit(n) :保留前n个元素

    5.skip(n) :跳过前n个元素

    6.distinct() :剔除重复元素

    7.sorted() :将Comparable元素的流排序

    8.sorted(Comparator) :将流元素按Comparator排序

    9.peek(fun) :流不变,但会把每个元素传入fun执行,可以用作调试

三、 流的Terminal方法(终结操作)

(1)约简操作

    1.reduce(fun) :从流中计算某个值,接受一个二元函数作为累积器,从前两个元素开始持续应用它,累积器的中间结果作为第一个参数,流元素作为第二个参数

    2.reduce(a, fun) :a为幺元值,作为累积器的起点

    3.reduce(a, fun1, fun2) :与二元变形类似,并发操作中,当累积器的第一个参数与第二个参数都为流元素类型时,可以对各个中间结果也应用累积器进行合并,但是当累积器的第一个参数不是流元素类型而是类型T的时候,各个中间结果也为类型T,需要fun2来将各个中间结果进行合并

(2)收集操作

    1.iterator():

    2.forEach(fun):

    3.forEachOrdered(fun) :可以应用在并行流上以保持元素顺序

    4.toArray():

    5.toArray(T[] :: new) :返回正确的元素类型

    6.collect(Collector):

    7.collect(fun1, fun2, fun3) :fun1转换流元素;fun2为累积器,将fun1的转换结果累积起来;fun3为组合器,将并行处理过程中累积器的各个结果组合起来

(3)查找与收集操作

    1.max(Comparator):返回流中最大值

    2.min(Comparator):返回流中最小值

    3.count():返回流中元素个数

    4.findFirst() :返回第一个元素

    5.findAny() :返回任意元素

    6.anyMatch(Predicate) :任意元素匹配时返回true

    7.allMatch(Predicate) :所有元素匹配时返回true

    8.noneMatch(Predicate) :没有元素匹配时返回true

1、创建Stream

  1. public class TestStream01 {
  2. //创建stream
  3. @Test
  4. public void test01(){
  5. //1、可以通过Collection 系列集合提供的stream() 或parallelStream()
  6. List<String> list = new ArrayList<>();
  7. Stream<String> stream1 = list.stream();
  8. //2、通过Arrays中的静态方法stream() 获取数组流
  9. Employee[] emps = new Employee[10];
  10. Stream<Employee> stream2 = Arrays.stream(emps);
  11. //3、通过Stream 类中的静态方法的of()
  12. Stream<String> stream3 = Stream.of("aa", "bb", "cc");
  13. //4、创建无线流
  14. //迭代
  15. Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
  16. stream4.limit(10).forEach(System.out::println);
  17. //生成
  18. Stream.generate(() -> Math.random())
  19. .forEach(System.out::println);
  20. }
  21. }

2、中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则,中间操作不会执行任何的处理!而在终止操作是一次性全部处理,称为“惰性求值”; 

     筛选与切片

     filter---接收Lambda,从流中排除某些元素

     limit---截断流,使其元素不超过给定数量

     skip(n)---跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补

    distinct---筛选,通过流所生成元素的hasCode() 和 equals()去除重复元素

映射

map--接收Lambda, 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

flatmap--接收一个函数作为参数,将流中的每个值换成另一个流,然后把所有的流连接成一个流。 

  1. @Test
  2. public void test06(){
  3. List<String> list = Arrays.asList("aaa","bbb","cccc","dddd");
  4. list.stream()
  5. .map((str) -> str.toUpperCase())
  6. .forEach(System.out::println);
  7. System.out.println("-----------------------------------------");
  8. employees.stream()
  9. .map(Employee::getAge)
  10. .forEach(System.out::println);
  11. System.out.println("-----------------------------------------");
  12. Stream<Stream<Character>> stream = list.stream()
  13. .map(TestStreamAPI02::filterCharacter);//{{a,a,a},{b,b,b}}
  14. stream.forEach((sm) ->{
  15. sm.forEach(System.out::println);
  16. });
  17. System.out.println("-----------------------------------------");
  18. Stream<Character> characterStream = list.stream()
  19. .flatMap(TestStreamAPI02::filterCharacter);//{a,a,a,b,b,b}
  20. characterStream.forEach(System.out::println);
  21. }
  22. public static Stream<Character> filterCharacter(String str){
  23. List<Character> list = new ArrayList<>();
  24. for (Character ch : str.toCharArray()) {
  25. list.add(ch);
  26. }
  27. return list.stream();
  28. }

排序

sorted()-自然排序

sorted(Comparator com) --定制排序

  1. @Test
  2. public void test07(){
  3. employees.stream().sorted((e1,e2) ->{
  4. if (e1.getAge().equals(e2.getAge())){
  5. return e1.getName().compareTo(e2.getName());
  6. }else {
  7. return e1.getAge().compareTo(e2.getAge());
  8. }
  9. }).forEach(System.out::println);
  10. }

3、终止操作(终端操作) 

查找与匹配

allMatch--检查是都匹配所有元素

anyMatch--检查是否至少匹配一个元素

noneMatch--检查是否没有匹配所有元素

findFrist--返回第一个元素

findAny--返回当前流中的任意元素

count--返回流中元素的总个数

max--返回流中最大值

min--返回流中最小值

  1. List<Employee> employees = Arrays.asList(
  2. new Employee("wts1",21,22222.2, Employee.Status.BUSY),
  3. new Employee("wts2",22,32222.2, Employee.Status.FREE),
  4. new Employee("wts3",23,42222.2, Employee.Status.VOCATION),
  5. new Employee("wts4",24,52222.2,Employee.Status.VOCATION)
  6. );
  7. @Test
  8. public void test01(){
  9. boolean b = employees.stream()
  10. .allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
  11. System.out.println(b);
  12. boolean b1 = employees.stream()
  13. .anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
  14. System.out.println(b1);
  15. boolean b2 = employees.stream()
  16. .noneMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
  17. System.out.println(b2);
  18. boolean b3 = employees.stream()
  19. .noneMatch((e) -> e.getStatus().equals(Employee.Status.VOCATION));
  20. System.out.println(b3);
  21. Optional<Employee> first = employees.stream()
  22. .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
  23. .findFirst();
  24. System.out.println(first.get());
  25. Optional<Employee> any = employees.stream()
  26. .filter((e) -> e.getStatus().equals(Employee.Status.FREE))
  27. .findAny();
  28. System.out.println(any);
  29. }
  30. @Test
  31. public void test02(){
  32. long count = employees.stream().count();
  33. System.out.println(count);
  34. Optional<Employee> max = employees.stream()
  35. .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
  36. System.out.println(max.get());
  37. Optional<Double> min = employees.stream()
  38. .map(Employee::getSalary)
  39. .min(Double::compare);
  40. System.out.println(min.get());
  41. }

归约

reduce(T identity , BinaryOperator)/reduce(BinaryOperator)  ---可以将流中元素反复结合起来,得到一个值。

  1. @Test
  2. public void test03(){
  3. List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
  4. Integer sum = list.stream()
  5. .reduce(0, (x, y) -> x + y);
  6. System.out.println(sum);
  7. System.out.println("----------------------------------------");
  8. Optional<Double> op = employees.stream()
  9. .map(Employee::getSalary)
  10. .reduce(Double::sum);
  11. System.out.println(op.get());
  12. }

收集

collect---将流转化为其他形式。接收一个Collector接口的实现,用于给Stream中元素汇总的方法。

  1. @Test
  2. public void test04(){
  3. List<String> collect = employees.stream()
  4. .map(Employee::getName)
  5. .collect(Collectors.toList());
  6. collect.forEach(System.out::println);
  7. System.out.println("----------------------------------");
  8. Set<String> collect1 = employees.stream()
  9. .map(Employee::getName)
  10. .collect(Collectors.toSet());
  11. collect1.forEach(System.out::println);
  12. System.out.println("---------------------------------");
  13. HashSet<String> collect2 = employees.stream()
  14. .map(Employee::getName)
  15. .collect(Collectors.toCollection(HashSet::new));
  16. collect2.forEach(System.out::println);
  17. }
  18. @Test
  19. public void test05(){
  20. //总数
  21. Long count = employees.stream()
  22. .collect(Collectors.counting());
  23. System.out.println(count);
  24. System.out.println("------------------------------------");
  25. //平均值
  26. Double avg = employees.stream()
  27. .collect(Collectors.averagingDouble(Employee::getSalary));
  28. System.out.println(avg);
  29. //总和
  30. DoubleSummaryStatistics sum = employees.stream()
  31. .collect(Collectors.summarizingDouble(Employee::getSalary));
  32. System.out.println(sum);
  33. //最大值
  34. Optional<Employee> max = employees.stream()
  35. .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
  36. System.out.println(max.get());
  37. //最小值
  38. Optional<Double> collect = employees.stream()
  39. .map(Employee::getSalary)
  40. .collect(Collectors.minBy(Double::compare));
  41. System.out.println(collect.get());
  42. }

分组

  1. //分组
  2. @Test
  3. public void test06(){
  4. Map<Employee.Status, List<Employee>> map = employees.stream()
  5. .collect(Collectors.groupingBy(Employee::getStatus));
  6. System.out.println(map);
  7. }
  8. //多级分组
  9. @Test
  10. public void test07(){
  11. Map<Employee.Status, Map<String, List<Employee>>> map = employees.stream()
  12. .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
  13. if (e.getAge() <= 35) {
  14. return "青年";
  15. } else if (e.getAge() <= 50) {
  16. return "老年";
  17. } else {
  18. return "老年";
  19. }
  20. })));
  21. System.out.println(map);
  22. }

分区

  1. @Test
  2. public void test08(){
  3. Map<Boolean, List<Employee>> map = employees.stream()
  4. .collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
  5. System.out.println(map);
  6. }
  7. @Test
  8. public void test09(){
  9. DoubleSummaryStatistics collect = employees.stream()
  10. .collect(Collectors.summarizingDouble(Employee::getSalary));
  11. System.out.println(collect.getMax());
  12. System.out.println(collect.getAverage());
  13. System.out.println(collect.getMin());
  14. System.out.println(collect.getSum());
  15. }
  16. @Test
  17. public void test10(){
  18. String collect = employees.stream()
  19. .map(Employee::getName)
  20. .collect(Collectors.joining(",","==","=="));
  21. System.out.println(collect);
  22. }

六、并行流与顺序流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据流的流

Java8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。stream API 可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换

  1. package com.wts.mybatisplus.jdk8.forkjoin;
  2. import java.util.concurrent.RecursiveTask;
  3. /**
  4. * @author WangTianShun
  5. * @date 2020/10/23 12:58
  6. */
  7. public class ForkJoinCalculate extends RecursiveTask<Long> {
  8. private long start;
  9. private long end;
  10. public ForkJoinCalculate(long start, long end) {
  11. this.start = start;
  12. this.end = end;
  13. }
  14. private static final long THRESHOLD = 10000;
  15. @Override
  16. protected Long compute() {
  17. long length = end - start;
  18. if (length <= THRESHOLD){
  19. long sum = 0;
  20. for (long i = start; i<= end; i++){
  21. sum += i;
  22. }
  23. return sum;
  24. }else {
  25. long middle = (start + end)/2;
  26. ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
  27. left.fork();//拆分子任务,同时压入线程队列
  28. ForkJoinCalculate right = new ForkJoinCalculate(middle+1,end);
  29. right.fork();
  30. return left.join() + right.join();
  31. }
  32. }
  33. }
  1. package com.wts.mybatisplus.jdk8.forkjoin;
  2. import org.junit.Test;
  3. import java.time.Duration;
  4. import java.time.Instant;
  5. import java.util.concurrent.ForkJoinPool;
  6. import java.util.concurrent.ForkJoinTask;
  7. import java.util.stream.LongStream;
  8. /**
  9. * @author WangTianShun
  10. * @date 2020/10/23 13:37
  11. */
  12. public class TestForkJoin {
  13. public static void main(String[] args) {
  14. Instant start = Instant.now();
  15. ForkJoinPool pool = new ForkJoinPool();
  16. ForkJoinTask<Long> task = new ForkJoinCalculate(0,100000000L);
  17. Long invoke = pool.invoke(task);
  18. System.out.println(invoke);
  19. Instant end = Instant.now();
  20. System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());
  21. }
  22. //普通
  23. @Test
  24. public void test02(){
  25. Instant start = Instant.now();
  26. long sum = 0L;
  27. for (long i = 0; i <= 100000000L; i++){
  28. sum += i;
  29. }
  30. System.out.println(sum);
  31. Instant end = Instant.now();
  32. System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());
  33. }
  34. //java8并行流
  35. @Test
  36. public void test3(){
  37. Instant start = Instant.now();
  38. LongStream.rangeClosed(0,100000000L)
  39. .parallel()
  40. .reduce(0,Long::sum);
  41. Instant end = Instant.now();
  42. System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());
  43. }
  44. }

七、Optional类

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或者不存在,原来null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常

常用方法: 

Optional.of(T t): 创建一个Optional实例 * Optional.empty(): 创建一个空的Optional实例

Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例

isPresent(): 判断是否包含值 * orElse(T t):如果调用对象包含值,返回该值,否则返回t

orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s的获取值

map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()

flatMap(Function mapper):与map 类似,要求返回值必须是Optional

1.创建optional对象,一般用ofNullable()而不用of():

    (1)empty() :用于创建一个没有值的Optional对象:Optional<String> emptyOpt = Optional.empty();

    (2)of() :使用一个非空的值创建Optional对象:Optional<String> notNullOpt = Optional.of(str);

    (3)ofNullable() :接收一个可以为null的值:Optional<String> nullableOpt = Optional.ofNullable(str);

    2.判断Null:

    (1)isPresent():如果创建的对象实例为非空值的话,isPresent()返回true,调用get()方法会返回该对象,如果没有值,调用isPresent()方法会返回false,调用get()方法抛出NullPointerException异常。

    3.获取对象:

    (1)get()

    4.使用map提取对象的值,如果我们要获取User对象中的roleId属性值,常见的方式是先判断是否为null然后直接获取,但使用Optional中提供的map()方法可以以更简单的方式实现

    5.使用orElse方法设置默认值,Optional类还包含其他方法用于获取值,这些方法分别为:

    (1)orElse():如果有值就返回,否则返回一个给定的值作为默认值;

    (2)orElseGet():与orElse()方法作用类似,区别在于生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值;

    (3)orElseThrow():与前面介绍的get()方法类似,当值为null时调用这两个方法都会抛出NullPointerException异常,区别在于该方法可以指定抛出的异常类型。

    6.使用filter()方法过滤,filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤,在代码中,如果filter()方法中的Lambda表达式成立,filter()方法会返回当前Optional对象值,否则,返回一个值为空的Optional对象。:

  1. @SpringBootTest
  2. public class TestOptional {
  3. @Test
  4. public void test1(){
  5. Optional<Employee> op = Optional.of(new Employee());
  6. System.out.println(op.get());
  7. }
  8. @Test
  9. public void test2(){
  10. Optional<Employee> op = Optional.empty();
  11. System.out.println(op.get());
  12. }
  13. @Test
  14. public void test3(){
  15. Optional<Employee> op = Optional.ofNullable(new Employee());
  16. /*if (op.isPresent()){
  17. System.out.println(op.get());
  18. }*/
  19. Employee emp = op.orElse( new Employee("wts1",21,22222.2));
  20. System.out.println(emp);
  21. System.out.println("--------------------------");
  22. Employee emp1 = op.orElseGet(() -> new Employee());
  23. System.out.println(emp1);
  24. }
  25. @Test
  26. public void test04(){
  27. Optional<Employee> op = Optional.ofNullable(new Employee("wts1",21,22222.2));
  28. Optional<String> s = op.map((e) -> e.getName());
  29. System.out.println(s.get());
  30. System.out.println("---------------------------");
  31. Optional<String> s1 = op.flatMap((e) -> Optional.of(e.getName()));
  32. System.out.println(s1.get());
  33. }
  34. }

八、接口中的默认方法与静态方法

Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求,就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个default关键字即可实现默认方法。为什么要有这个特性?以前当需要修改接口的时候,需要修改全部实现该接口的类。而引进的默认方法的目的是为了解决接口的修改与现有的实现不兼容的问题。

  1. public interface MyFun {
  2. default String getName(){
  3. return "哈哈哈";
  4. }
  5. public static void show(){
  6. System.out.println("接口中的静态方法");
  7. }
  8. }

当出现这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,这种情况的解决方法:

    1.是创建自己的默认方法,来覆盖重写接口的默认方法

    2.可以使用 super 来调用指定接口的默认方法

    Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。默认方法允许继续使用现有的Java接口,而同时能够保障正常的编译过程。尽管默认方法非常强大,但是在使用默认方法时我们需要小心注意一个地方:在声明一个默认方法前,请仔细思考是不是真的有必要使用默认方法,因为默认方法会带给程序歧义,并且在复杂的继承体系中容易产生编译错误。

九、新时间日期API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。在旧版的 Java 中,日期时间 API 存在诸多问题,比如:

        1.非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

        2.设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

        3.时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

   Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

       1.Local(本地) − 简化了日期时间的处理,没有时区的问题。

       2.Zoned(时区) − 通过制定的时区处理日期时间。

    新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

  1. package com.wts.mybatisplus.jdk8.date;
  2. import org.junit.Test;
  3. import org.springframework.boot.test.context.SpringBootTest;
  4. import javax.xml.bind.SchemaOutputResolver;
  5. import java.time.*;
  6. import java.util.function.DoubleToIntFunction;
  7. /**
  8. * @author WangTianShun
  9. * @date 2020/10/23 22:16
  10. */
  11. @SpringBootTest
  12. public class TestLocalDateTime {
  13. //1、LocalDate 2、LocalTime 3、LocalDateTime
  14. @Test
  15. public void test(){
  16. LocalDateTime ldt = LocalDateTime.now();
  17. System.out.println(ldt);
  18. LocalDateTime ldt2 = LocalDateTime.of(2019,10,19,13,22,33);
  19. System.out.println(ldt2);
  20. LocalDateTime ldt3 = ldt.plusYears(2);
  21. System.out.println(ldt3);
  22. LocalDateTime ldt4 = ldt.minusMonths(2);
  23. System.out.println(ldt4);
  24. System.out.println(ldt.getYear());
  25. System.out.println(ldt.getMonth());
  26. System.out.println(ldt.getDayOfMonth());
  27. System.out.println(ldt.getHour());
  28. System.out.println(ldt.getMinute());
  29. System.out.println(ldt.getSecond());
  30. }
  31. //2、Instant:时间戳(以Unix元年 1970年1月1日00:00:00到某个时间之间的毫秒值)
  32. @Test
  33. public void test2(){
  34. Instant now = Instant.now();//默认获取UTC时区
  35. System.out.println(now);
  36. OffsetDateTime time = now.atOffset(ZoneOffset.ofHours(8));
  37. System.out.println(time);
  38. }
  39. //3、Duration : 计算两个“时间”之间的间隔
  40. //Period: 计算两个日期之间的间隔
  41. @Test
  42. public void test3(){
  43. Instant ins1 = Instant.now();
  44. try {
  45. Thread.sleep(1000);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. Instant ins2 = Instant.now();
  50. Duration duration = Duration.between(ins1, ins2);
  51. System.out.println(duration.toMillis());
  52. System.out.println("------------------------");
  53. LocalTime lt1 = LocalTime.now();
  54. try {
  55. Thread.sleep(1000);
  56. } catch (InterruptedException e) {
  57. e.printStackTrace();
  58. }
  59. LocalTime lt2 = LocalTime.now();
  60. Duration duration1 = Duration.between(ins1, ins2);
  61. System.out.println(duration1.toMillis());
  62. }
  63. }

十、注解相关

(1)可以进行重复注解

自从Java 5引入了注解机制,这一特性就变得非常流行并且广为使用。然而,使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明多次。Java 8打破了这条规则,引入了重复注解机制,这样相同的注解可以在同一地方声明多次。

重复注解机制本身必须用@Repeatable注解。事实上,这并不是语言层面上的改变,更多的是编译器的技巧,底层的原理保持不变。

(2)扩展注解的支持

Java 8扩展了注解的上下文。现在几乎可以为任何东西添加注解:局部变量、泛型类、父类与接口的实现,就连方法的异常也能添加注解。

十一、Java8 中 List 转 Map(Collectors.toMap)

在实际项目中我们经常会用到 List 转 Map 操作,在过去我们可能使用的是 for 循环遍历的方式。举个例子:

先定义类:

  1. // 简单对象
  2. @Accessors(chain = true) // 链式方法
  3. @lombok.Data
  4. class User {
  5. private String id;
  6. private String name;
  7. }

然后有这样一个 List:

  1. List<User> userList = Lists.newArrayList(
  2. new User().setId("A").setName("张三"),
  3. new User().setId("B").setName("李四"),
  4. new User().setId("C").setName("王五")
  5. );

我们希望转成 Map 的格式为:

  1. A-> 张三
  2. B-> 李四
  3. C-> 王五

过去的做法(循环):

  1. Map<String, String> map = new HashMap<>();
  2. for (User user : userList) {
  3. map.put(user.getId(), user.getName());
  4. }

使用 Java8 特性

Java8 中新增了 Stream 特性,使得我们在处理集合操作时更方便了。

以上述例子为例,我们可以一句话搞定:

userList.stream().collect(Collectors.toMap(User::getId, User::getName));

当然,如果希望得到 Map 的 value 为对象本身时,可以这样写:

  1. userList.stream().collect(Collectors.toMap(User::getId, t -> t));
  2. 或:
  3. userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

关于 Collectors.toMap 方法

Collectors.toMap 有三个重载方法:

  1. toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
  2. toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
  3. BinaryOperator<U> mergeFunction);
  4. toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
  5. BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);

参数含义分别是:

  1. keyMapper:Key 的映射函数

  2. valueMapper:Value 的映射函数

  3. mergeFunction:当 Key 冲突时,调用的合并方法

  4. mapSupplier:Map 构造器,在需要返回特定的 Map 时使用

还是用上面的例子,如果 List 中 userId 有相同的,使用上面的写法会抛异常:

  1. List<User> userList = Lists.newArrayList(
  2. new User().setId("A").setName("张三"),
  3. new User().setId("A").setName("李四"), // Key 相同
  4. new User().setId("C").setName("王五")
  5. );
  6. userList.stream().collect(Collectors.toMap(User::getId, User::getName));
  7. // 异常:
  8. java.lang.IllegalStateException: Duplicate key 张三
  9. at java.util.stream.Collectors.lambda$throwingMerger$114(Collectors.java:133)
  10. at java.util.HashMap.merge(HashMap.java:1245)
  11. at java.util.stream.Collectors.lambda$toMap$172(Collectors.java:1320)
  12. at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
  13. at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
  14. at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
  15. at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  16. at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
  17. at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  18. at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  19. at Test.toMap(Test.java:17)
  20. ...

这时就需要调用第二个重载方法,传入合并函数,如:

  1. userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2));
  2. // 输出结果:
  3. A-> 张三李四
  4. C-> 王五

第四个参数(mapSupplier)用于自定义返回 Map 类型,比如我们希望返回的 Map 是根据 Key 排序的,可以使用如下写法:

  1. List<User> userList = Lists.newArrayList(
  2. new User().setId("B").setName("张三"),
  3. new User().setId("A").setName("李四"),
  4. new User().setId("C").setName("王五")
  5. );
  6. userList.stream().collect(
  7. Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1, TreeMap::new)
  8. );
  9. // 输出结果:
  10. A-> 李四
  11. B-> 张三
  12. C-> 王五

JAVA8 lambda表达式中Collectors.toMap()方法

例:

Map<Long, VipEntity> vipMap = vipList.stream().collect(Collectors.toMap(VipEntity::getUserId, v -> v, (v1, v2) -> v1));

说明:

.collect(Collectors.toMap(VipEntity::getUserId, v -> v, (v1, v2) -> v1))

第一个参数 VipEntity::getUserId 表示选择 VipEntity 的 getUserId 作为map的key值;
第二个参数 v -> v 表示选择将原来的对象作为map的value值;
第三个参数 (v1, v2) -> v1 中,如果v1与v2的key值相同,选择v1作为那个key所对应的value值。

Function.identity()的含义

Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法。
Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t形式的Lambda表达式

  1. private static void identity() {
  2. Stream<String> stream = Stream.of("I", "love", "you", "too");
  3. Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
  4. System.out.println(map);
  5. }

日常开发中,我们很多时候需要用到Java 8Lambda表达式,它允许把函数作为一个方法的参数,让我们的代码更优雅、更简洁。所以整理了一波工作中,我常用的,有哪些Lambda表达式。看完一定会有帮助的。

1. list转map

工作中,我们经常遇到listmap的案例。Collectors.toMap就可以把一个list数组转成一个Map。代码如下:

  1. // 1、list 转 map
  2. @Test
  3. void listConvertMap() {
  4. List<UserInfo> userInfoList = new ArrayList<>();
  5. userInfoList.add(new UserInfo(1L, "捡田螺的小男孩", 18));
  6. userInfoList.add(new UserInfo(2L, "程序员田螺", 27));
  7. userInfoList.add(new UserInfo(2L, "捡瓶子的小男孩", 26));
  8. /**
  9. * 使用Collectors.toMap的时候,如果有可以重复会报错,所以需要加(k1, k2) -> k1
  10. * (k1, k2) -> k1 表示,如果有重复的key,则保留第一个,舍弃第二个
  11. */
  12. Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId, userInfo -> userInfo, (k1, k2) -> k1));
  13. userInfoMap.values().forEach(a -> System.out.println(a.getUserName()));
  14. }

类似的,还有Collectors.toList()Collectors.toSet(),表示把对应的流转化为list或者Set。 

 2. filter()过滤

从数组集合中,过滤掉不符合条件的元素,留下符合条件的元素。

  1. // 2、filter过滤
  2. @Test
  3. void filterTest() {
  4. List<UserInfo> userInfoList = new ArrayList<>();
  5. userInfoList.add(new UserInfo(1L, "捡田螺的小男孩", 18));
  6. userInfoList.add(new UserInfo(2L, "程序员田螺", 27));
  7. userInfoList.add(new UserInfo(3L, "捡瓶子的小男孩", 26));
  8. /**
  9. * filter 过滤,留下超过18岁的用户
  10. */
  11. List<UserInfo> userInfoResultList = userInfoList.stream().filter(user -> user.getAge() > 18).collect(Collectors.toList());
  12. userInfoResultList.forEach(a -> System.out.println(a.getUserName()));
  13. }

3. foreach遍历

foreach 遍历list,遍历map,真的很丝滑。

  1. // 3、foreach遍历
  2. @Test
  3. void foreachTest() {
  4. /**
  5. * forEach 遍历集合List列表
  6. */
  7. List<String> userNameList = Arrays.asList("捡田螺的小男孩", "程序员田螺", "捡瓶子的小男孩");
  8. userNameList.forEach(System.out::println);
  9. System.out.println("===================");
  10. HashMap<String, String> hashMap = new HashMap<>();
  11. hashMap.put("公众号", "捡田螺的小男孩");
  12. hashMap.put("职业", "程序员田螺");
  13. hashMap.put("昵称", "捡瓶子的小男孩");
  14. /**
  15. * forEach 遍历集合Map
  16. */
  17. hashMap.forEach((k, v) -> System.out.println(k + ":\t" + v));
  18. }

4. groupingBy分组

提到分组,相信大家都会想起SQLgroup by。我们经常需要一个List做分组操作。比如,按城市分组用户。在Java8之前,是这么实现的:

  1. // 4、groupingBy
  2. @Test
  3. void groupingByTest() {
  4. List<UserInfo> originUserInfoList = new ArrayList<>();
  5. originUserInfoList.add(new UserInfo(1L, "捡田螺的小男孩", 18, "深圳"));
  6. originUserInfoList.add(new UserInfo(3L, "捡瓶子的小男孩", 26, "湛江"));
  7. originUserInfoList.add(new UserInfo(2L, "程序员田螺", 27, "深圳"));
  8. Map<String, List<UserInfo>> result = new HashMap<>();
  9. for (UserInfo userInfo : originUserInfoList) {
  10. String city = userInfo.getCity();
  11. List<UserInfo> userInfos = result.get(city);
  12. if (userInfos == null) {
  13. userInfos = new ArrayList<>();
  14. result.put(city, userInfos);
  15. }
  16. userInfos.add(userInfo);
  17. }
  18. result.forEach((k, v) -> System.out.println(k + ":\t" + v));
  19. }

而使用Java8的groupingBy分组器,清爽无比:

  1. Map<String, List<UserInfo>> result = originUserInfoList.stream()
  2. .collect(Collectors.groupingBy(UserInfo::getCity));

 

5. sorted+Comparator 排序

工作中,排序的需求比较多,使用sorted+Comparator排序,真的很香。

  1. // 5、sorted + Comparator 排序
  2. @Test
  3. void sorted() {
  4. List<UserInfo> userInfoList = new ArrayList<>();
  5. userInfoList.add(new UserInfo(1L, "捡田螺的小男孩", 18));
  6. userInfoList.add(new UserInfo(3L, "捡瓶子的小男孩", 26));
  7. userInfoList.add(new UserInfo(2L, "程序员田螺", 27));
  8. /**
  9. * sorted + Comparator.comparing 排序列表,
  10. */
  11. userInfoList = userInfoList.stream().sorted(Comparator.comparing(UserInfo::getAge)).collect(Collectors.toList());
  12. userInfoList.forEach(a -> System.out.println(a.toString()));
  13. System.out.println("开始降序排序");
  14. /**
  15. * 如果想降序排序,则可以使用加reversed()
  16. */
  17. userInfoList = userInfoList.stream().sorted(Comparator.comparing(UserInfo::getAge).reversed()).collect(Collectors.toList());
  18. userInfoList.forEach(a -> System.out.println(a.toString()));
  19. }

 

6.distinct去重

distinct可以去除重复的元素:

  1. // 6.distinct去重
  2. @Test
  3. void distinctTest() {
  4. List<String> list = Arrays.asList("A", "B", "F", "A", "C");
  5. List<String> temp = list.stream().distinct().collect(Collectors.toList());
  6. temp.forEach(System.out::println);
  7. }

 

7. findFirst 返回第一个

findFirst 很多业务场景,我们只需要返回集合的第一个元素即可:

  1. // 7、findFirst
  2. @Test
  3. void findFirstTest(){
  4. List<String> list = Arrays.asList("A", "B", "F", "A", "C");
  5. list.stream().findFirst().ifPresent(System.out::println);
  6. }

8. anyMatch是否至少匹配一个元素

anyMatch 检查流是否包含至少一个满足给定谓词的元素。

  1. // 8、anyMatch
  2. @Test
  3. void anyMatchTest() {
  4. Stream<String> stream = Stream.of("A", "B", "C", "D");
  5. boolean match = stream.anyMatch(s -> s.contains("C"));
  6. System.out.println(match);
  7. }

9. allMatch 匹配所有元素

allMatch 检查流是否所有都满足给定谓词的元素。

  1. // 9、 allMatch 匹配所有元素
  2. @Test
  3. void allMatchTest() {
  4. Stream<String> stream = Stream.of("A", "B", "C", "D");
  5. boolean match = stream.allMatch(s -> s.contains("C"));
  6. System.out.println(match);
  7. }

10. map转换

map方法可以帮我们做元素转换,比如一个元素所有字母转化为大写,又或者把获取一个元素对象的某个属性,demo如下:

  1. // 10、map转换
  2. @Test
  3. void mapConvert() {
  4. List<String> list = Arrays.asList("jay", "tianluo");
  5. //转化为大写
  6. List<String> upperCaselist = list.stream().map(String::toUpperCase).collect(Collectors.toList());
  7. upperCaselist.forEach(System.out::println);
  8. }

11. Reduce

Reduce可以合并流的元素,并生成一个值

  • T reduce(T identity, BinaryOperator accumulator);
    • 该方法第一个参数表示默认值
    • 在2的基础上加上 1,2,3,4,5
    • 代码示例
  1. @Test
  2. void reduceTest() {
  3. int sum = Stream.of(1, 2, 3, 4).reduce(2, (a, b) -> a + b);
  4. System.out.println(sum);
  5. }

12. peek 打印个日志

peek()方法是一个中间Stream操作,有时候我们可以使用peek来打印日志。

  1. // 12、peek 打印个日志
  2. // peek()方法是一个中间Stream操作,有时候我们可以使用peek来打印日志。
  3. @Test
  4. void peekTest() {
  5. List<String> result = Stream.of("程序员田螺", "捡田螺的小男孩", "捡瓶子的小男孩")
  6. .filter(a -> a.contains("田螺"))
  7. .peek(a -> System.out.println("公众号:" + a)).collect(Collectors.toList());
  8. System.out.println(result);
  9. }

 ​​​​​​

13. Max,Min最大最小

使用lambda流求最大,最小值,非常方便。

  1. // 13、Max,Min最大最小
  2. @Test
  3. void maxTest() {
  4. List<UserInfo> userInfoList = new ArrayList<>();
  5. userInfoList.add(new UserInfo(1L, "捡田螺的小男孩", 18));
  6. userInfoList.add(new UserInfo(3L, "捡瓶子的小男孩", 26));
  7. userInfoList.add(new UserInfo(2L, "程序员田螺", 27));
  8. Optional<UserInfo> maxAgeUserInfoOpt = userInfoList.stream().max(Comparator.comparing(UserInfo::getAge));
  9. maxAgeUserInfoOpt.ifPresent(userInfo -> System.out.println("max age user:" + userInfo));
  10. Optional<UserInfo> minAgeUserInfoOpt = userInfoList.stream().min(Comparator.comparing(UserInfo::getAge));
  11. minAgeUserInfoOpt.ifPresent(userInfo -> System.out.println("min age user:" + userInfo));
  12. }

14. count统计

一般count()表示获取流数据元素总数。

  1. // 14、count统计
  2. @Test
  3. void countTest() {
  4. List<UserInfo> userInfoList = new ArrayList<>();
  5. userInfoList.add(new UserInfo(1L, "捡田螺的小男孩", 18));
  6. userInfoList.add(new UserInfo(3L, "捡瓶子的小男孩", 26));
  7. userInfoList.add(new UserInfo(2L, "程序员田螺", 27));
  8. long count = userInfoList.stream().filter(user -> user.getAge() > 18).count();
  9. System.out.println("大于18岁的用户:" + count);
  10. }

 ​

15. 常用函数式接口

其实lambda离不开函数式接口,我们来看下JDK8常用的几个函数式接口:

  • Function<T, R>(转换型): 接受一个输入参数,返回一个结果

  • Consumer<T> (消费型): 接收一个输入参数,并且无返回操作

  • Predicate<T> (判断型): 接收一个输入参数,并且返回布尔值结果

  • Supplier<T> (供给型): 无参数,返回结果

Function<T, R> 是一个功能转换型的接口,可以把将一种类型的数据转化为另外一种类型的数据

  1. // 15、常用函数式接口
  2. @Test
  3. void functionTest() {
  4. //获取每个字符串的长度,并且返回
  5. Function<String, Integer> function = String::length;
  6. Stream<String> stream = Stream.of("程序员田螺", "捡田螺的小男孩", "捡瓶子的小男孩");
  7. Stream<Integer> resultStream = stream.map(function);
  8. resultStream.forEach(System.out::println);
  9. }

Consumer<T>是一个消费性接口,通过传入参数,并且无返回的操作 

  1. // Consumer<T>是一个消费性接口,通过传入参数,并且无返回的操作 
  2. @Test
  3. void consumerTest() {
  4. Consumer<String> consumer = System.out::println;
  5. Stream<String> stream = Stream.of("程序员田螺", "捡田螺的小男孩", "捡瓶子的小男孩");
  6. stream.forEach(consumer);
  7. }

 Predicate<T>是一个判断型接口,并且返回布尔值结果.

  1. // Predicate<T>是一个判断型接口,并且返回布尔值结果.
  2. @Test
  3. void testPredicate() {
  4. //获取每个字符串的长度,并且返回
  5. Predicate<Integer> predicate = a -> a > 18;
  6. UserInfo userInfo = new UserInfo(2L, "程序员田螺", 27);
  7. System.out.println(predicate.test(userInfo.getAge()));
  8. }

 

Supplier<T>是一个供给型接口,无参数,有返回结果。 

  1. // Supplier<T>是一个供给型接口,无参数,有返回结果。
  2. @Test
  3. void testSupplier() {
  4. Supplier<Integer> supplier = () -> Integer.valueOf("666");
  5. System.out.println(supplier.get());
  6. }

 这几个函数在日常开发中,也是可以灵活应用的,比如我们DAO操作完数据库,是会有个result的整型结果返回。我们就可以用Supplier<T>来统一判断是否操作成功。如下:

  1. private void saveDb(Supplier<Integer> supplier) {
  2. if (supplier.get() > 0) {
  3. System.out.println("插入数据库成功");
  4. }else{
  5. System.out.println("插入数据库失败");
  6. }
  7. }
  8. @Test
  9. public void add() throws Exception {
  10. Course course=new Course();
  11. course.setCname("java");
  12. course.setUserId(100L);
  13. course.setCstatus("Normal");
  14. saveDb(() -> courseMapper.insert(course));
  15. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53223
推荐阅读
相关标签
  

闽ICP备14008679号