标签: java lambda


Java8新特性—lambda表达式

对lambda介绍

  1. 1. lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。
  2. 2. Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。

lambda表达式基础语法

  1. 1. 关于箭头操作符:
  2. Java8中引入了一个新的操作符,"->",该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分;
  3. 2. 左侧:
  4. Lambda表达式的参数列表,对应的是接口中抽象方法的参数列表;
  5. 3. 右侧:
  6. Lambda表达式中所需要执行的功能(Lambda体),对应的是对抽象方法的实现;(函数式接口(只能有一个抽象方法))
  7. 4. Lambda表达式的实质--对接口的实现;
  1. 语法格式:

    • 接口中的抽象方法 : 无参数,无返回值;
      例如:runnable接口中的方法,使用lambda实现方式Runnable r1 = () -> System.out.println("Hello world!");
  • 接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)
    例如:下面两种方式都是正确的方式// Consumer<String>con = (x) -> System.out.println(x); Consumer<String>con = x -> System.out.println(x);
  • 两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return
    例如:Comparator<Integer>com = (x,y) -> { System.out.println("函数式接口"); return Integer.compare(y,x); //降序 };
  • 两个参数,有返回值,但是只有一条语句: 大括号省略,return省略
    例如:Comparator<Integer>com = (x,y) -> Integer.compare(x,y);//升序
  • Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即”类 型推断”,
    例如:(Integer x,Integer y ) -> Integer.compare(x,y)可以简写成(x,y) -> Integer.compare(x,y);

lambda表达式函数式接口

Java8新特性 - 图1

函数式接口表示在接口内只有一个抽象方法;可以使用注解@FunctionlInterface来标识,可以检查是否是函数式接口;
Java中自带的有四个函数式接口:
(1)Consumer con 消费性接口 void accept(T t);
(2)Function fun 函数式接口 R appliy(T t);
(3)Predicate pre 断言形接口 boolean test(T t);
(4)Spplier sup 供给型接口 T get();
Java中所给的四个函数式接口可以直接使用,也可以使用自定义的函数式接口,但是必须按照函数式接口 规范定义,函数中只有一个方法,且使用注解检查是否符合函数式标准。

方法引用和构造器引用

  1. 1. 方法引用
  2. 使用前提: Lambda体中调用方法的参数列表和返回值类型,要和函数式接口中抽象方法的参数列表和返回 值类型保持一致;
  3. 2. 语法格式:
  4. 1)、对象::实例方法名
  5. 2)、类名::静态方法
  6. 3)、类::实例方法名
  7. 使用注意: Lambda参数列表中的第一个参数是实例方法的第一个调用者,而第二个参数是实例方法的参数时,可以使用ClassName :: method
  8. 3. 构造器引用
  9. 需要调用构造器的参数列表,要与函数式接口中的抽象方法的参数列表保持一致;

实体类中修饰时间戳

  1. 使用 LocalDateTime 并且加上注解@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  2. @TableLogic 在实体类中标志逻辑删除

Stream

引入流

  1. 使用流与不使用流的区别
  1. 例子:需要筛选出一份菜单中卡路里<400的菜的名字
  2. public class Code_01_Java7AndJava8Compare {
  3. public static void main(String[] args) {
  4. // 返回 热量<400 的菜肴 的 名称, 返回结果按照从低到高排序, Java7的写法
  5. System.out.println(java7());
  6. System.out.println(java8());
  7. }
  8. static List<String> java7(){
  9. List<Dish> lowCaloricDishes = new ArrayList<>();
  10. for (Dish d : Dish.menu) {
  11. if (d.getCalories() < 400) {
  12. lowCaloricDishes.add(d);
  13. }
  14. }
  15. Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
  16. public int compare(Dish d1, Dish d2) {
  17. return Integer.compare(d1.getCalories(), d2.getCalories());
  18. }
  19. });
  20. List<String> lowCaloricDishesName = new ArrayList<>();
  21. for (Dish d : lowCaloricDishes) {
  22. lowCaloricDishesName.add(d.getName());
  23. }
  24. return lowCaloricDishesName;
  25. }
  26. static List<String> java8(){
  27. List<String> lowCaloricDishesName =
  28. Dish.menu.stream()
  29. .filter(d -> d.getCalories() < 400)
  30. .sorted(Comparator.comparing(Dish::getCalories))
  31. .map(Dish::getName)
  32. .collect(Collectors.toList());
  33. return lowCaloricDishesName;
  34. }
  35. }
  1. 流的概述
  • 流简短的定义: 是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列;
  • 元素序列 — 就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序 值。因为集合是数据结构,所以它的主要目的是以特定的时间/ 空间复杂度存储和访问元 素(如ArrayList 与 LinkedList) 。但流的目的在于表达计算,比如你前面见到的 filter、 sorted和 map。集合讲的是数据,流讲的是计算。
  • 源 — 流会使用一个提供数据的源,如集合、数组或输入/输出资源;
  • 数据处理操作 — 流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中 的常用操作,如filter、 map、 reduce、 find、 match、 sort等;
  • 流的重要的特点
  • 流水线 — 很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大 的流水线;流水线的操作可以看作对数据源进行数据库式查询;
  • 内部迭代 — 与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的;
  • ①Stream自己不会存储元素;② Stream不会改变原对象,相反,他们会返回一个持有结果的新Stream;③Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行;

Java8新特性 - 图2

  1. 流与集合
    1)、只能遍历一次
    请注意,和迭代器类似,流只能遍历一次

    • 遍历完之后,我们就说这个流已经被消费掉了;
    • 你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样(这里假设它是集 合之类的可重复的源,如果是I/O通道就没戏了);
      2)、外部迭代与内部迭代
    • 使用Collection接口需要用户去做迭代(比如用for-each) ,这称为外部迭代;
    • 相反, Streams库使用内部迭代;
      外部迭代和内部迭代的区别:
      Java8新特性 - 图3
  2. 流操作
    主要分为两种:一种为中间操作,另一种为终端操作;
    Java8新特性 - 图4
    1)、中间操作
    中间操作就是产生的结果(仍然是一个流)。
    诸如filter或 sorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。
  1. // 除非流水线上触发一个终端操作,否则中间操作不会执行任何处理
  2. // 流的延迟性质。
  3. public class Code_02_StreamDelayFeature {
  4. public static void main(String[] args) {
  5. List<String> names =
  6. Dish.menu.stream()
  7. .filter(d -> {
  8. System.out.println("filtering" + d.getName());
  9. return d.getCalories() > 300;
  10. })
  11. .map(d -> {
  12. System.out.println("mapping" + d.getName());
  13. return d.getName();
  14. })
  15. .limit(3)
  16. .collect(Collectors.toList());
  17. System.out.println(names);
  18. // // 终端操作
  19. // System.out.println("-------Terminal Operation------");
  20. // Dish.menu.stream().forEach(System.out::println);
  21. }
  22. }

2)、终端操作
终止操作会从流的流水线生成结果。其结果是任何不是流的值,比如List、 Integer,甚至 void。
3)、流的使用步骤
流的流水线背后的理念类似于构建器模式。

三个基本步骤:

  • 创建Stream : 需要一个数据源(如:集合,数组),获取一个流;
  • 中间操作: 一个中间操作链,对数据源的数据进行处理;
  • 终止操作(终端操作): 一个终止操作,执行中间操作链,并产生结果;

使用流

  1. 构建流
    构建的流的方式有:
  • 从Collection中构建;

  • 从值value(Stream.of())中构建;

  • 从数组中构建(Arrays.stream());

  • 从文件中构建;

  • 由函数生成: 创建无限流;

创建的几种方法的示例代码:

  1. /** 创建流的几种方法 */
  2. public class Code_03_CreateStream {
  3. public static void main(String[] args) throws IOException {
  4. PrintStream out = System.out;
  5. out.println("--------fromCollection--------");
  6. fromCollection().forEach(x -> out.print(x + " ")); // 从Collection中创建Stream
  7. out.println("\n" + "-------fromValues---------");
  8. fromValues().forEach(x -> out.print(x + " ")); // 从Collection中创建Stream
  9. out.println("\n" + "--------fromArrays--------");
  10. fromArrays().forEach(x -> out.print(x + " ")); // 从Collection中创建Stream
  11. out.println("\n" + "--------fromFile--------");
  12. fromFile().forEach(out::println); // 从函数中创建
  13. out.println("\n" + "--------fromIterate--------");
  14. fromIterate().forEach(x -> out.print(x + " ")); // 从函数中创建
  15. out.println("\n" + "--------fromGenerate--------");
  16. fromGenerate().forEach(x -> out.print(x + " ")); // 从函数中创建
  17. out.println("\n" + "--------fromCustom--------");
  18. fromCustom().forEach(x -> out.print(x + " "));
  19. out.println("\n" + "----------------");
  20. }
  21. static Stream<String> fromCollection() {
  22. List<String> list = Arrays.asList("aa", "bb", "cc");
  23. return list.stream();
  24. }
  25. static Stream<String> fromValues() {
  26. return Stream.of("aa", "bb", "cc");
  27. }
  28. static Stream<String> fromArrays() {
  29. String[] str = {"aa", "bb", "cc"};
  30. return Arrays.stream(str);
  31. }
  32. static Stream<String> fromFile() throws IOException {
  33. Path path = Paths.get("/home/zxzxin/Main.java");
  34. Stream<String> stream = Files.lines(path);
  35. return stream;
  36. }
  37. static Stream fromIterate() {
  38. return Stream.iterate(0, n -> n + 2).limit(5); // 函数创建的无限流
  39. }
  40. static Stream<Double> fromGenerate(){
  41. return Stream.generate(Math::random).limit(5);
  42. }
  43. // 创建Custom的流 (CusSupplier)
  44. static Stream<Custom>fromCustom(){
  45. return Stream.generate(new CusSupplier()).limit(5);
  46. }
  47. static class CusSupplier implements Supplier<Custom> {
  48. private int index = 0;
  49. private Random random = new Random(System.currentTimeMillis());
  50. @Override
  51. public Custom get() {
  52. index = random.nextInt(100);
  53. return new Custom(index, "name-" + index);
  54. }
  55. }
  56. static class Custom {
  57. private int id;
  58. private String name;
  59. public Custom(int id, String name) {
  60. this.id = id;
  61. this.name = name;
  62. }
  63. @Override
  64. public String toString() {
  65. return "Obj{" +
  66. "name='" + name + '\'' +
  67. ", id=" + id +
  68. '}';
  69. }
  70. }
  71. }

输出:

  1. --------fromCollection--------
  2. aa bb cc
  3. -------fromValues---------
  4. aa bb cc
  5. --------fromArrays--------
  6. aa bb cc
  7. --------fromFile--------
  8. import java.io.*;
  9. import java.util.*;
  10. public class Main {
  11. public static void main(String[] args) {
  12. Scanner in = new Scanner(new BufferedInputStream(System.in));
  13. PrintStream out = System.out;
  14. }
  15. }
  16. --------fromIterate--------
  17. 0 2 4 6 8
  18. --------fromGenerate--------
  19. 0.18018050075496417 0.948721748467966 0.37983036182518304 0.679145483357325 0.21520045208568783
  20. --------fromCustom--------
  21. Obj{name='name-73', id=73} Obj{name='name-84', id=84} Obj{name='name-14', id=14} Obj{name='name-79', id=79} Obj{name='name-51', id=51}
  22. ----------------
  1. filter、limit、skip、map、flatMap

    • filter : 该操作会接受一个谓词(一个返回boolean的函数)(Predicate)作为参数,并返回一个包括所有符合谓词的元素的流;
    • limit : 流支持limit(n)方法,该方法会返回一个不超过给定长度的流;
    • skip : 流还支持skip(n)方法,返回一个扔掉了前n 个元素的流;
    • map : 流支持map方法,它会接受一个函数(Function)作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”);
    • flatMap (扁平化): flatmap()方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接 起来成为一个流;
  2. match、find、reduce
  • match:查看元素是否匹配(返回boolean),包括allMatch(), anyMatch()、noneMatch();
  • find :
  • isPresent()将在Optional包含值的时候返回true, 否则返回false;
  • ifPresent(Consumer block)会在值存在的时候执行给定的代码块;
  • T get()会在值存在时返回值,否则抛出一个NoSuchElement异常;
  • T orElse(T other)会在值存在时返回值,否则返回一个默认值;
  • Optional of(T value) : 通过value构造一个Optional;
  1. public class Code_05_StreamOperations2 {
  2. static PrintStream out;
  3. public static void main(String[] args) {
  4. out = System.out;
  5. out.println("-------matchTest---------");
  6. matchTest();
  7. out.println("\n" + "-------findTest---------");
  8. findTest();
  9. out.println("\n" + "-------reduceTest---------");
  10. reduceTest();
  11. }
  12. static void matchTest(){
  13. List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
  14. out.println(arr.stream().allMatch(i -> i > 10));
  15. out.println(arr.stream().anyMatch(i -> i > 6));
  16. out.println(arr.stream().noneMatch(i -> i < 0));
  17. }
  18. static void findTest(){
  19. List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
  20. Optional<Integer> any = arr.stream().filter(i -> i % 2 == 0).findAny();
  21. out.println(any.get());
  22. Optional<Integer> first = arr.stream().filter(i -> i % 2 == 0).findFirst();
  23. first.ifPresent(out::println);
  24. out.println(first.get()); //没有就抛出 NoSuchElementException
  25. out.println(first.orElse(-1)); // 如果first为空就输出-1
  26. System.out.println(first.filter(i -> i == 2).get()); // Optional还会产生一个stream
  27. System.out.println(find(arr, -1, i -> i > 10)); // 自己写的一个防止空指针的,而Optional中有一个已经存在的
  28. }
  29. static int find(List<Integer> values, int defaultValue, Predicate<Integer> predicate){
  30. for(int val : values){
  31. if(predicate.test(val))
  32. return val;
  33. }
  34. return defaultValue;
  35. }
  36. // reduce 也是一个terminal的操作
  37. static void reduceTest(){
  38. List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
  39. System.out.println(arr.stream().reduce(0, (a, b) -> a + b)); //计算数组的和 ,有初始值就是Integer
  40. arr.stream().reduce((a, b) -> a + b).ifPresent(out::println); // 没有初始值就是 Optional
  41. // 提取所有的偶数相乘
  42. int res = arr.stream().filter(x -> x%2 == 0).reduce(1, (a, b) -> a*b);
  43. Optional.of(res).ifPresent(out::println);
  44. }
  45. }

输出:

  1. -------matchTest---------
  2. false
  3. true
  4. true
  5. -------findTest---------
  6. 2
  7. 2
  8. 2
  9. 2
  10. 2
  11. -1
  12. -------reduceTest---------
  13. 28
  14. 28
  15. 48
  1. 数值流
    Java 8引入了三个原始类型特化流接口来解决这个问题:IntStream、 DoubleStream和LongStream,分别将流中的元素特化为int、 long和 double,从而避免了暗含的装箱成本。这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性——即类似int和 Integer之间的效率差异。
    映射方法:
  • 映射到数值流
  • 将流转换为特化版本的常用方法是mapToInt、 mapToDouble和 mapToLong;
  • 例如mapToInt返回一个IntStream(而不是一个Stream);
  • 转换回对象流
  • 使用boxed()方法;
  • 用处: 例如,IntStream上的操作只能产生原始整数。

Optional取代null

Optional取代null

用流收集数据

Java8中的时间表示

  • 分为四类:String、Instant、LocalDateTime、ZonedDateTime
  • String是格式化的时间,Instant是时间戳,LocalDateTime是不含时区信息的时间,ZonedDateTime是含有时区信息的时间

乐观锁 与 悲观锁

乐观锁

概述

  1. 乐观锁其实表示

悲观锁

作者 @zzxhub