java8对核心类库的改进主要包括集合类的API和新引入的流(Stream)
流使程序员得以站在更高层次上对集合进行操作
介绍Stream类中的一组方法,每个方法都对应集合上的一种操作
外部迭代->内部迭代
计算伦敦艺术家人数:
int count = 0;
for(Artist artist : allArtists){
if(artist.isFrom("London")){
count++;
}
}
在集合上进行迭代,对每一个返回元素再进行处理
问题:
每次迭代集合时,都需要写很多样板代码,影响代码可读性
for循环是一个封装了迭代的语法糖,首先调用iterator方法产生一个新的Interator对象,
进而控制整个迭代过程,这就是外部迭代
迭代过程通过显式调用Interator对象的hasNext和next方法完成迭代
int count = 0;
Interator interator = allArtists.interator();
while(interator.hasNext()){
Artist artist = interator.next();
if(artist.isFrom("London")){
count++;
}
}
外部迭代的问题:
本质上是一种串行操作
使用for循环会将行为和方法混为一谈
内部迭代:
另一种迭代方式就是内部迭代
long count = allArtists.stream()
.filter(artist -> artist.isFrom("London"))
.count();
stream()方法和interator()方法作用一样,
该方法返回的并不是一个Interator对象,而是返回内部迭代中的相应接口Stream
Stream是用函数式编程方式在集合类上进行复杂操作的工具
可被分解为两步:
1,filter(artist -> artist.isFrom(“London”))找不所有来自伦敦的艺术家
2,count()计算他们的人数
每种操作都对应Stream接口的一个方法
找出来自伦敦的艺术家,需要对Stream对象进行过滤:filter
(过滤指:只保留通过某项测试的对象,测试由函数完成返回true或false)
count()方法计算给点Stream中包含的对象数量
实现机制
整个过程被分解为两种简单操作:过滤和计数
问题:两种操作是否需要两次循环?
事实上,返回的Stream对象不是一个新的集合,而是创建一个新集合的配方
allArtists.stream()
.filter(artist -> artist.isFrom("London"))
filter只描述了stream,没有产生新集合,这种方法称为惰性求值方法
而count方法最终会从Stream产生值,这种方法称为及早求值方法
long count = allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London")
});
在过路器中加入一句打印输出艺术家名字,运行代码不会输出任何信息
long count = allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London")
})
.count();
在加入一个拥有终止操作的流,此时计数操作和艺术家名字都会被输出
只需要看返回值即可,
若返回值是Stream就是惰性求值方法,
若返回值是一个值或空,就是及早求值方法
整个操作类似于建造者模式,使用一系列操作设置属性和配置,最后调用build方法对象才被真正创建
由Stream里的值生成一个列表,是一个及早求值操作
Stream的of方法使用一组初始值生成新的Stream
List collected = Stream.of("a", "b", "c")
.collect(Collectors.toList());
首先由列表生成一个Stream,然后进行Stream的collect操作,由Stream生成列表
使用for循环将字符串转换为大写
List collected = new ArrayList<>();
for(String string : asList("a", "b", "hello")){
String uppercaseString = string.toUpperCase();
collected.add(uppercaseString);
}
使用map操作将字符创转换为大写
List collected = Stream.of("a", "b", "hello")
.map(string -> string.toUpperCase())
.collect(collectors.toList)
Lambda表达式必须是Function接口的一个实例,
Function接口是只包含一个参数的普通函数接口,参数和返回值不必属于同一类型
遍历数据并检查元素时使用
使用for循环遍历列表,使用条件语句做判断:
List beginningWithNumbers = new ArrayList<>();
for(String value : asList("a", "1abc", "abc1")){
if(isDigit(value.charAt(0))){// 数字开头
beginningWithNumbers.add(value);
}
}
使用函数式风格:
List beginningWithNumbers = Stream.of("a", "1abc", "abc1")
.filter(value -> isDigit(value.charAt(0)))
.collect(toList())
for循环中的if是一个很强的信号,可使用filter方法替代
和map相似,filter接受一个函数作为参数,该函数使用Lambda表达式表示,值为true的元素会被保留下来
该Lambda表达式的函数接口是Predicate接口(T->Predicate->boolean)
将多个Stream连接成一个Stream
List together = Stream.of(asList(1, 2), asList(3, 4))
.flatMap(numbers -> numbers.stream())
.collect(toList());
调用.stream()方法将每个列表转换成Stream对象,其余部分由flatMap方法处理
flatMap方法的函数接口也是Function,方法的返回值为Stream类型
在Stream上求最大值和最小值
使用for循环,根据曲目长度,查找最短曲目
List
min()根据曲目长度,查找最短曲目
List
使用Comparator对象中java8新提供的静态方法comparing,实现一个比较器
comparing方法接受一个函数并返回另一个函数
还可以调用空Stream的max方法,返回Optional对象
Optional对象表示一个可能存在也可能不存在的值
如果Stream为空,该值不存在,如果不为空,则该值存在
通过调用get方法可以取出Optional对象中的值
reduce操作可以实现从一组值中生成一个值
count,min,max都是reduce方法
reduce的累加过程:
每一步都将Stream中的元素进行累加,遍历至Stream中的最后一个元素时,得到累加值
使用reduce求和:
int count = Stream.of(1, 2, 3)
.reduce(0, (acc, element) -> acc + element);
传入Stream的两个参数分别是:当前元素和acc,
acc是累加器,保存当前累加结果
Lambda表达式返回值为最新的acc,上一轮acc的值和当前元素的相加结果
reduce的类型是BinaryOperator
展开reduce操作:
BinaryOperator accumulator = (acc, element) -> acc + element
int count = accumuator.apply(
accumuator.apply(
accumuator.apply(0, 1),
2),
3);
命令式编程求和:
int acc = 0;
for(Integer element : asList(1, 2, 3)){
acc = acc = element;
}
对于集合来说,循环在外部,且需要手动更新