《Effective Java》知识点(6)--Lambda和Stream

42. Lambda优先于匿名类

       Lambda代码比匿名类更加简洁。删除所有Lambda参数的类型,编译器会利用一个称作类型推导的过程,根据上下文推断出这些类型。不要给函数对象使用匿名类,除非必须创建非函数接口的类型的实例。

       Lambda的局限:

       a.  Lambda没有名称和文档;如果一个计算本身不是自描述的,或者超出了几行(一到三行是合理的),那就不要把它放在一个Lambda中。

       b. Lambda限于函数接口,不能创建实例,而匿名类可以。

       c. Lambda无法获得对自身的引用,而匿名类可以。

43. 方法引用优先于Lambda

      方法引用常常比Lambda表达式更加简洁、清晰。建议优先用方法引用,只有当它不够简洁时才用Lambda。

      五种方法引用:

方法引用类型范例Lambda等式说明
静态Integer::parseIntstr->Integer.parseInt(str)函数对象与被引用方法带有相同的参数
有限制Instant.now()::isAfter

Instant then = Instant.now();

t->then.isAfter(t)

类似于静态引用
无限制String::toLowerCasestr->str.toLowerCase()在声明函数前面添加一个参数来指定接收对象。常用于流管道。
类构造器TreeMap<K,V>::new()->new TreeMap<K,V>充当工厂对象
数组构造器int[]::newlen->new int[len]充当工厂对象
44. 坚持使用标准的函数接口

44.1 只要标准的函数接口能够满足需求,应该优先考虑,而不是专门再构建一个新的函数接口。

        6个基础接口:

接口函数签名范例
UnaryOperator<T>T apply(T t)String::toLowerCase
BinaryOperator<T>T apply(T t1,T t2)BigInteger::add
Predicate<T>boolean test(T t)Collection::isEmpty
Function<T,R>R apply(T t)Arrays::asList
Supplier<T>T get()Instant::now
Consumer<T>void accept(T t)System.out::println

44.2 千万不要用带包装类型的基础函数接口来代替基本函数接口。使用装箱基本类型进行批量操作处理,最终会导致致命的性能问题。

44.3 函数接口具有以下一个或多个特征,需要自己编写。记住它是接口,应谨慎设计。

       a. 通用,并且将受益于表述性的名称

       b. 具有与其关联的严格的契约

       c. 将受益于定制的缺省方法

必须始终用@FunctionalInterface注解对自己编写的函数接口进行标注。另外不要在相同的参数位置,提供不同的函数接口来进行多次重载(可能导致歧义)。

44.4 注解@FunctionalInterface的作用:

         a. 告诉他人这个接口是针对Lambda设计的

         b. 这个接口不会进行编译,除非它只有一个抽象方法

         c. 避免后续维护人员不小心给该接口添加抽象方法

45. 谨慎使用Stream

      Stream API简化了串行或并行的大批量操作。但是滥用Stream会使程序代码难以读懂和维护。

45.1 使用Stream注意事项

         a. 不能忘记终止操作,否则流管道(Stream pipeline)将是一个静默的无操作指令。 

         b. 在没有显式类型的情况下,仔细命名Lambda参数,对流管道的可读性至关重要。

         c. 在流管道中使用helper方法,有利于增强可读性。

         d. 最好避免利用Stream来处理char值(可能变int值) 。

         e. 重构现有的代码来使用Stream,只在必要是时候才在新代码中使用。

45.2 适合Stream完成的工作

         流管道利用函数对象(Lambda或方法引用)来描述重复的计算。

         a. 统一转换元素的序列

         b.  过滤元素的序列

         c.  利用单个操作(如添加、连接或者计算其最小值)合并元素的顺序

         d. 搜索满足某些条件的元素的序列

45.3 适合迭代代码完成的工作

         a. 从代码块中,可以读取或者修改范围内的任意局部变量;而Lambda不能修改任何局部变量,只能读取final或有效的final变量

         b. 从代码块中,可以从外围方法中return、break或continue外围循环,或者抛出该方法声明要抛出的任何受检异常;而Lambda无法完成这些事情。

45.4  如果不能确定用Stream还是用迭代比较好,那么两种都试试,看哪种更好用。许多任务是结合使用两种方法来一起完成的。

46. 优先选择Stream中无副作用的函数

      Stream范型最重要的部分是把计算构造成一系列变型,每一级结果都尽可能靠近上一级结果的纯函数。纯函数是指结果只取决于输入的函数:它不依赖任何可变的状态,也不更新任何状态。 

       foreach操作应该只用于报告Stream计算的结果,而不是执行计算。

       静态导入Collectors的所有成员是明智的,可以提升流管道的可读性。最重要的收集器工厂是toList、toSet、toMap、groupingBy和joining,必须了解它们。

47. Stream要优先用Collection作为返回类型

      Collection接口是Iterable的一个子类型,它有一个stream方法,提供了迭代和stream访问。对于公共的、返回序列的方法,Collection或者适当的子类型通常是最佳的返回类型。

      数组也通过Arrays.asList和Stream.of方法提供了简单的迭代和Stream访问。如果返回的序列足够小,容易存储,或许最好返回标准的集合实现,如ArrayList或者HashSet。但是千万别在内存中保存巨大的序列,将它作为集合返回即可。

     在编写返回一系列元素的方法时,记住优先返回集合,如果无法返回集合,就返回Stream或者Iterable,方便使用即可。

48. 谨慎使用Stream并行

      并行Stream不仅可能降低性能,包括活性失败,还可能导致结果出错,以及难于预计的行为。

      在Stream上通过并行获得性能,最好是通过ArrayList、HashMap、HashSet和ConcurrentHashMap实例,数组,int范围和long范围等。这些数据结构都可以被精确、轻松地分成任意大小的子范围,使并行线程中的分工变得更轻松;另外它们提供了优异的引用局部性:序列化的元素一起保存在内存中。

      尽量不要并行Stream,除非有足够的理由相信它能保证计算的正确性,并且加快程序的运行速度。并发运行时一定要确保代码正确,并在真实环境下认真地进行性能测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值