java8新特性—— Lambda表达式

系统管理员 2022-12-27 01:18 212阅读 0赞

Lambda表达式

用来替代匿名函数,可以将一个函数赋值给一个变量作为参数传入另一个函数,java的闭包

原则:可推导就是可省略,比如说参数类型,返回值

  1. // 1. 不需要参数,返回值为 5 {}只有一行代码,可以省略
  2. () -> 5
  3. // 2. 接收一个参数(数字类型),返回其2倍的值,()只有一个参数可以省略
  4. x -> 2 * x
  5. // 3. 接受2个参数(数字),并返回他们的差值
  6. (x, y) -> x y
  7. // 4. 接收2个int型整数,返回他们的和
  8. (int x, int y) -> x + y
  9. // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
  10. (String s) -> System.out.print(s)

语法

Interface var = (x,y) -> {}

该接口只能有一个需要被实现的方法,小括号中参数取决于Interface 的接口方法的参数,没有参数则为空,{}中为方法的实现内容,如果内容只有一行代码,{}可以省略。实际上就是匿名函数

  1. Runnable run = new Runnable(){
  2. @Override
  3. publicvoidrun(){
  4. System.out.println("常规写法");
  5. }
  6. };
  7. Runnable run1 = () -> {System.out.println("lambda");};//{}中只有一条语句时,{}可以省略
  8. //匿名函数的访问权限可以省略(跟接收变量的作用域保持一致,返回值和参数类型都可以编译器自动判断。)

只有一个抽象方法需要被实现的接口,称为“函数式接口”,为了避免后续被人在该接口中添加方法,导致规则被破坏,可以在该接口上加一个声明@FunctionalInterface,这样该接口就无法添加新的接口函数了

变量作用域

lambda 表达式只能引用final 类型的外层局部变量,就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。与匿名函数同理

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

  1. int num = 1;
  2. Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
  3. s.convert(2);
  4. num = 5;
  5. //报错信息:Local variable num defined in an enclosing scope must be final or effectively final
  6. //在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
  7. String first = "";
  8. Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错

方法引用

若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”,可以理解为方法引用是lambda表达式的另外一种表达形式

主要有三种语法格式:

  • 对象 :: 实例方法名
  • 类 :: 静态方法名
  • 类 :: 实例方法名

被引用的方法的参数和返回值必须和要实现的抽象方法的参数和返回值一致

静态方法引用

  1. //格式:Classname :: staticMethodName 和静态方法调用相比,只是把 . 换为 ::
  2. String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
  3. Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y);

实例对象方法引用

  1. //格式:instanceReference::methodName
  2. class ComparisonProvider{
  3. public int compareByName(Person a, Person b){
  4. return a.getName().compareTo(b.getName());
  5. }
  6. public int compareByAge(Person a, Person b){
  7. return a.getBirthday().compareTo(b.getBirthday());
  8. }
  9. }
  10. ComparisonProvider myComparisonProvider = new ComparisonProvider();
  11. Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

超类上的实例方法引用

//格式:super::methodName

//还可以使用this

泛型类和泛型方法引用

  1. public interface MyFunc<T> {
  2. int func(T[] als, T v);
  3. }
  4. public class MyArrayOps {
  5. public static <T> int countMatching(T[] vals, T v) {
  6. int count = 0;
  7. for (int i = 0; i < vals.length; i++) {
  8. if (vals[i] == v) count++;
  9. }
  10. return count;
  11. }
  12. }
  13. public class GenericMethodRefDemo {
  14. public static <T> int myOp(MyFunc<T> f, T[] vals, T v) {
  15. return f.func(vals, v);
  16. }
  17. public static void main(String[] args){
  18. Integer[] vals = {1, 2, 3, 4, 2, 3, 4, 4, 5};
  19. String[] strs = {"One", "Two", "Three", "Two"};
  20. int count;
  21. count=myOp(MyArrayOps::<Integer>countMatching, vals, 4);
  22. System.out.println("vals contains "+count+" 4s");
  23. count=myOp(MyArrayOps::<String>countMatching, strs, "Two");
  24. System.out.println("strs contains "+count+" Twos");
  25. }
  26. }

构造器引用

通过函数式接口实例化类时可以用构造器引用,引用到的是方法参数个数和类型匹配的构造器

  1. //格式:ClassName :: new,调用默认构造器。
  2. //lambda方式
  3. Supplier<Passenger> supplier1 = () -> new Passenger();
  4. //构造器引用:通过类型推断,引用无参构造器
  5. Supplier<Passenger> supplier2 = Passenger::new;
  6. //lambda方式
  7. BiFunction<String, String, Passenger> function1 = (x, y) -> new Passenger(x, y);
  8. //构造器引用:通过类型推断,引用有两个String参数的构造器
  9. BiFunction<String, String, Passenger> function2 = Passenger::new;

数组引用

  1. //lambda方式
  2. Function<Integer, String[]> fun1 = (x) -> new String[x];
  3. String[] strs1 = fun1.apply(10);
  4. //数组引用
  5. Function<Integer, String[]> fun2 = String[]::new;
  6. String[] strs2 = fun2.apply(10);

Stream

A sequence of elements supporting sequential and parallel aggregate operations.

1、Stream是元素的集合,这点让Stream看起来用些类似Iterator;

2、可以支持顺序和并行的对原Stream进行汇聚的操作;

特性:

  • 不存储数据
  • 不改变源数据
  • 延迟执行

使用步骤:

  • 创建Stream数据源;
  • 数据处理,转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换);
  • 对Stream进行聚合(Reduce)操作,获取想要的结果;

创建数据源:

1、Collection.stream(); 从集合获取流。

2、Collection.parallelStream(); 从集合获取并行流。

3、Arrays.stream(T array) or Stream.of(); 从数组获取流。

4、BufferedReader.lines(); 从输入流中获取流。

5、IntStream.of() ; 从静态方法中获取流。

6、Stream.generate(); 自己生成流

  1. @Test
  2. public void createStream() throws FileNotFoundException {
  3. List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
  4. String[] nameArr = {"Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter"};
  5. // 集合获取 Stream 流
  6. Stream<String> nameListStream = nameList.stream();
  7. // 集合获取并行 Stream 流
  8. Stream<String> nameListStream2 = nameList.parallelStream();
  9. // 数组获取 Stream 流
  10. Stream<String> nameArrStream = Stream.of(nameArr);
  11. // 数组获取 Stream 流
  12. Stream<String> nameArrStream1 = Arrays.stream(nameArr);
  13. // 文件流获取 Stream 流
  14. BufferedReader bufferedReader = new BufferedReader(new FileReader("README.md"));
  15. Stream<String> linesStream = bufferedReader.lines();
  16. // 从静态方法获取流操作
  17. IntStream rangeStream = IntStream.range(1, 10);
  18. rangeStream.limit(10).forEach(num -> System.out.print(num+","));
  19. System.out.println();
  20. IntStream intStream = IntStream.of(1, 2, 3, 3, 4);
  21. intStream.forEach(num -> System.out.print(num+","));
  22. }

数据处理/转换

中间操作,可以有多个,返回的是一个新的stream对象,惰性计算,只有在开始收集结果时中间操作才会生效。

map (mapToInt, flatMap ):把对象映射成另一种对象

  1. @Test
  2. public void mapTest() {
  3. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  4. // 映射成 2倍数字
  5. List<Integer> collect = numberList.stream()
  6. .map(number -> number * 2)
  7. .collect(Collectors.toList());
  8. collect.forEach(number -> System.out.print(number + ","));
  9. System.out.println();
  10. numberList.stream()
  11. .map(number -> "数字 " + number + ",")
  12. .forEach(number -> System.out.println(number));
  13. }
  14. @Test
  15. public void flatMapTest() {
  16. Stream<List<Integer>> inputStream = Stream.of(
  17. Arrays.asList(1),
  18. Arrays.asList(2, 3),
  19. Arrays.asList(4, 5, 6)
  20. );
  21. List<Integer> collect = inputStream
  22. .flatMap((childList) -> childList.stream())
  23. .collect(Collectors.toList());
  24. collect.forEach(number -> System.out.print(number + ","));
  25. }
  26. // 输出结果
  27. // 1,2,3,4,5,6,
  28. filter:数据筛选,相当于if判断
  29. @Test
  30. public void filterTest() {
  31. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  32. List<Integer> collect = numberList.stream()
  33. .filter(number -> number % 2 == 0)
  34. .collect(Collectors.toList());
  35. collect.forEach(number -> System.out.print(number + ","));
  36. }
  37. distinct:去重
  38. public void distinctTest() {
  39. List<String> list = Arrays.asList("AA", "BB", "CC", "BB", "CC", "AA", "AA");
  40. long l = list.stream().distinct().count();
  41. System.out.println("count:"+l);
  42. String output = list.stream().distinct().collect(Collectors.joining(","));
  43. System.out.println(output);
  44. }
  45. sorted
  46. peek
  47. limit:获取前n个元素
  48. skip:丢弃前n个元素
  49. @Test
  50. public void limitOrSkipTest() {
  51. List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
  52. ageList.stream()
  53. .limit(3)
  54. .forEach(age -> System.out.print(age+","));、//11,22,13
  55. System.out.println();
  56. ageList.stream()
  57. .skip(3)
  58. .forEach(age -> System.out.print(age+","));//14,25,26
  59. }
  60. parallel:并行流
  61. public void parallelTest(){
  62. Long resourse = LongStream.rangeClosed(0,1000000000L)
  63. .parallel().reduce(0,Long::sum);
  64. System.out.println(resourse);
  65. }
  66. sequential
  67. unordered

聚合收集结果

stream处理的最后一步,执行完stream就被用尽了不能继续操作。

forEach:遍历stream,不能return/break,支持lambda

  1. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  2. numberList.stream().forEach(number -> System.out.println(number+","));
  3. forEachOrdered
  4. toArray
  5. reduce:累加器
  6. //reduce中返回的结果会作为下次累加器计算的第一个参数
  7. Optional accResult = Stream.of(1, 2, 3, 4).reduce((acc, item) -> {
  8. System.out.println("acc : " + acc);
  9. acc += item;
  10. System.out.println("item: " + item);
  11. System.out.println("acc+ : " + acc);
  12. System.out.println("--------");
  13. return acc;
  14. });
  15. collect
  16. min
  17. max
  18. count
  19. anyMatch
  20. allMatch
  21. noneMatch
  22. findFirst
  23. findAny
  24. iterator
  25. Statistics:统计
  26. @Test
  27. public void mathTest() {
  28. List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
  29. IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics();
  30. System.out.println("最小值:" + stats.getMin());
  31. System.out.println("最大值:" + stats.getMax());
  32. System.out.println("个数:" + stats.getCount());
  33. System.out.println("和:" + stats.getSum());
  34. System.out.println("平均数:" + stats.getAverage());
  35. }
  36. // 输出结果
  37. // 最小值:1
  38. // 最大值:6
  39. // 个数:6
  40. // 和:21
  41. // 平均数:3.5
  42. groupingBy:分组聚合,相当于mysqlgroup by
  43. @Test
  44. public void groupByTest() {
  45. List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
  46. Map<String, List<Integer>> ageGrouyByMap = ageList.stream()
  47. .collect(Collectors.groupingBy(age -> String.valueOf(age / 10)));
  48. ageGrouyByMap.forEach((k, v) -> {
  49. System.out.println("年龄" + k + "0多岁的有:" + v);
  50. });
  51. }
  52. // 输出结果
  53. // 年龄10多岁的有:[11, 13, 14]
  54. // 年龄20多岁的有:[22, 25, 26]
  55. partitioningBy:按条件分组
  56. @Test
  57. public void partitioningByTest() {
  58. List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
  59. Map<Boolean, List<Integer>> ageMap = ageList.stream()
  60. .collect(Collectors.partitioningBy(age -> age > 18));
  61. System.out.println("未成年人:" + ageMap.get(false));
  62. System.out.println("成年人:" + ageMap.get(true));
  63. }
  64. // 输出结果
  65. // 未成年人:[11, 13, 14]
  66. // 成年人:[22, 25, 26]

自己生成Stream

  1. @Test
  2. public void generateTest(){
  3. // 生成自己的随机数流
  4. Random random = new Random();
  5. Stream<Integer> generateRandom = Stream.generate(random::nextInt);
  6. generateRandom.limit(5).forEach(System.out::println);
  7. // 生成自己的 UUID 流
  8. Stream<UUID> generate = Stream.generate(UUID::randomUUID);
  9. generate.limit(5).forEach(System.out::println);
  10. }
  11. //使用limit进行短路

short-circuiting

有一种 Stream 操作被称作 short-circuiting ,它是指当 Stream 流无限大但是需要返回的 Stream 流是有限的时候,而又希望它能在有限的时间内计算出结果,那么这个操作就被称为short-circuiting。例如 findFirst操作。

findFirst:找出stream中第一个元素

  1. @Test
  2. public void findFirstTest(){
  3. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  4. Optional<Integer> firstNumber = numberList.stream()
  5. .findFirst();
  6. System.out.println(firstNumber.orElse(-1));
  7. }
  8. //找出第一个元素后就会停止遍历,相当于短路操作
  9. 解决终端操作只能一个的问题
  10. Supplier<Stream<String>> streamSupplier =
  11. () -> Stream.of("d2", "a2", "b1", "b3", "c")
  12. .filter(s -> s.startsWith("a"));
  13. streamSupplier.get().anyMatch(s -> true); // ok
  14. streamSupplier.get().noneMatch(s -> true); // ok

并行迭代器

  1. //tryAdvance 相当于普通迭代器iterator 串行处理
  2. public void iterator(){
  3. AtomicInteger num = new AtomicInteger(0);
  4. while(true){
  5. boolean flag = spliterator.tryAdvance((i) ->{
  6. num.addAndGet((int)i);
  7. System.out.println(i);
  8. });
  9. if(!flag){
  10. break;
  11. }
  12. }
  13. System.out.println(num);
  14. }
  15. //trySplit将list分段,每段单独处理,为并行提供可能
  16. public void spliterator(){
  17. AtomicInteger num = new AtomicInteger(0);
  18. Spliterator s1 = spliterator.trySplit();
  19. Spliterator s2 = spliterator.trySplit();
  20. spliterator.forEachRemaining((i) ->{
  21. num.addAndGet((int)i);
  22. System.out.println("spliterator:"+i);
  23. });
  24. s1.forEachRemaining((i) ->{
  25. num.addAndGet((int)i);
  26. System.out.println("s1:"+i);
  27. });
  28. s2.forEachRemaining((i) ->{
  29. num.addAndGet((int)i);
  30. System.out.println("s2:"+i);
  31. });
  32. System.out.println("最终结果:"+num);
  33. }
  34. //利用分段,开启多线程处理
  35. public void spliterator2() throws InterruptedException {
  36. CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
  37. run(spliterator.trySplit());
  38. return "future1 finished!";
  39. });
  40. CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
  41. run(spliterator.trySplit());
  42. return "future2 finished!";
  43. });
  44. CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
  45. run(spliterator);
  46. return "future3 finished!";
  47. });
  48. CompletableFuture<Void> combindFuture = CompletableFuture.allOf(future1, future2);
  49. try {
  50. combindFuture.get();
  51. } catch (InterruptedException e) {
  52. e.printStackTrace();
  53. } catch (ExecutionException e) {
  54. e.printStackTrace();
  55. }
  56. System.out.println("future1: " + future1.isDone() + " future2: " + future2.isDone());
  57. System.out.println("最终结果为:" + count);
  58. }
  59. public void run(Spliterator s1) {
  60. final String threadName = Thread.currentThread().getName();
  61. System.out.println("线程" + threadName + "开始运行-----");
  62. s1.forEachRemaining(new Consumer() {
  63. @Override
  64. public void accept(Object o) {
  65. count.addAndGet((Integer)o);
  66. }
  67. });
  68. System.out.println("线程" + threadName + "运行结束-----");
  69. }

HashMap

JDK8优化了HashMap的实现, 主要优化点包括:

  • 将链表方式修改成链表或者红黑树的形式
  • 修改resize的过程,解决JDK7在resize在并发场景下死锁的隐患
  • JDK1.7存储使用Entry数组, JDK8使用Node或者TreeNode数组存储

当链表长度大于8是链表的存储结构会被修改成红黑树的形式。

查询效率从O(N)提升到O(logN)。链表长度小于6时,红黑树的方式退化成链表。

JDK7链表插入是从链表头部插入, 在resize的时候会将原来的链表逆序。

JDK8插入从链表尾部插入, 因此在resize的时候仍然保持原来的顺序。

其他

新增JVM工具:jdeps提供了用于分析类文件的命令行工具。

使用metaSpace代替永久区

新增NMT(Native Memeory Trace)本地内存跟踪器

日期和时间api:

老版本的Date类有两个,java.util.Date和java.sql.Date线程不安全

发表评论

表情:
评论列表 (有 0 条评论,212人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JAVA8特性-lambda表达式

    摘要 一篇让你搞懂lambda表达式、函数式接口、方法引用、Optional这些JAVA8的新特性及相互间关系。下图是本文的内容概要 ![在这里插入图片描述][w...

    相关 Java 8特性lambda表达式

    lambda 表达式是 java 8th 给我们带来的几个重量级新特性之一,借用 lambda 表达式可以让我们的程序设计更加简洁。 一. 行为参数化 行为参数化简单的说就