音乐: Artist:创作音乐的个人或团队 name:艺术家名字 members:乐队成员 origin:乐队来自哪里 Track:(专辑中的一支曲目) name:曲目名称 Album:专辑 name:专辑名称 tracks:专辑上曲目列表 musicans:参与创作本专辑的艺术家列表
:::info
lambda表达式表现形式
- 表达式不包含参数,使用空括号
()
表示没有参数 - 表达式包含一个参数,可以省略参数的括号
- 表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号
{}
将代码块括起来。 - 表达式也可以包含多个参数的方法
- 表达式的参数类型由编译器推断出来,当然不错,但为了更高的可读性,建议显示声明
lambda表达式 无法使用非终态(非final)类型的变量。强行使用编译器会报错。
:::
集合上的数据过滤
filter :
:::success
filter刻画出了Stream,但没有产生新的集合,向filter这样只描述Stream不产生新集合的方法被称为惰性求值方法。
而像count最终会从Stream产生值的方法叫作及早求值方法。
:::
:::warning
及早求值与惰性求值判断:
只需要看返回值,如果返回值是Stream则为惰性求值,如果返回值是另一个值或者为null则为及早求值。
为什么要区分及早求值与惰性求值:
只有在对需要什么样的结果和操作有更多的了解,才能更有效的进行计算。
:::
常用的流操作
- collect(toList()) 由Stream里的值生成一个列表,是及早求值
- Stream的of方法使用一组初始值生成新的Stream
- map 是一个将一种类型值转换成另外一种类型的函数。可以理解成一种映射,而不是常见的key-value的数据结构。
- 传给map的lambda表达式只接受一个参数,参数和返回值不必属于同一种类型,但lambda表达式必须是Function接口的一个实例。如图3-4所示,Function接口是只包含一个参数的函数接口。
filter 遍历数据并检查其中的元素。
该模式的核心思想是保留Stream中的一些元素而过滤掉其他元素。和map很像,filter同样接收一个函数「方法」作为参数,该函数用lambda表达式表示,也即是Predicate接口。由于此方法与if语句的功能相同,因此其返回值只有true或false。返回的true的元素被保留。
- flatmap 可用Stream替换值,然后将多个Stream连接成一个Stream
实例参考
调用stream方法,将每个list转为stream对象,剩下的交给flatMap处理。flatMap方法相关函数接口和map一样,都是Function接口,只是方法返回值限定为stream
- max和min 求最大值和最小值
- 空Stream的max方法,返回Optional对象,它代表一个可能存在也可能不存在的值,如果Stream为空,那么该值不存在;如果不为空,则该值存在。
- reduce
- reduce操作可以实现从一组值生成一个值
- 整合操作
:::info
1.Album类有个getMusicians方法,该方法返回一个Stream对象,包含整张专辑中所有的表演者;
2.使用filter方法对表演者进行过滤,只保留乐队;
3.使用map方法将乐队映射为其所属国家;
4.使用collect(Collectors.toList())方法将国籍放入一个列表。
实例:从专辑中获取音乐时长大于一分钟的曲目名称
:::
- 高阶函数
:::info
高阶函数:指接受另外一个函数作为参数,或返回一个函数的函数。
如何辨认:看函数签名,如果函数参数列表中包含函数接口,或该函数返回一个函数接口。比如map函数就是高阶函数。 :::
:::success 为了减少基本类型与对象类型的装箱、拆箱的性能消耗,Stream类的某些方法对基本类型和装箱类型做了区分:
在java8中,只对整型、长整型、双浮点数做了特殊处理(因为这三种类型在计算方面用的最多,特殊处理后性能提高最明显)
对基本类型做特殊处理的方法在命名上有明确的规范。如果方法返回类型为基本类型,则在基本类型上加To,例如:图4-1:ToLongFunction
如果参数是基本类型,则不需要加前缀,只需要类型名称即可,如图4-2LongFunction:
如果是高阶函数,则操作后+To+基本类型,如mapToLong
这些基本类型都有与之对应的Stream,以基本类型为前缀,如LongStream
:::
Lambda表达式作为参数时,其类型由他的目标类型推导得出,推导过程遵循如下规则:
- 如果只有一个可能的目标类型,由相应函数接口里的参数类型推导得出。
- 如果有多个可能的目标类型,由最具体的类型推导得出。
- 如果有多个可能的类型且最具体的类型不明确,则需要人为指定类型。
FunctionalInterface接口
每个用作函数接口的接口都应该添加该注解。 java中有些接口虽然只有一个方法,但并不是为了使用lambda表达式来实现。可能纯属巧合,比如Comparable或Serializable。 但与Comparable和Serializable不同,为了提高Stream对象可操作性引入的各种新接口都需要有lambda表达式可以实现它,他们的意义在于将代码块作为数据打包起来。而且该注解会强制javac检查一个接口是否符合函数接口的标准。如果注解在枚举类型、类、或另个注解上,以及接口包含不止一个抽象方法,javac就会报错。
多重继承
接口允许多重继承,因此有可能碰到两个接口包含签名相同的默认方法的情况,那么他该继承哪个呢? 如果子类没有明确的指定实现哪个接口或者没有重写该接口,则javac在编译时会报错(解决方式:明确指定继承接口;重载实现接口方法)
三定律 :::info
- 类胜于接口。如果在继承链中有方法体或者抽象的方法声明,那么就忽略接口中定义的方法
- 子类胜于父类。如果一个接口继承了另一个接口,切两个接口都定义了一个默认方法,那么子类中定义的方法胜出。
- 没有规则三。如果上面两条规则不适用,子类要么需要实现该方法,要么将该方法声明为抽象方法。 :::
Optional
Optional是为核心类库新设计的一个数据类型,用来替换null值。
使用Option对象有两个目的:
- Optional对象鼓励成天你许愿适时检查变量是否为空,以避免代码缺陷
- 它将一个类的API中可能为null的值文档化,这比阅读实现代码要简单很多。
高级集合类和收集器