遗留代码:找出长度大于1分钟的歌曲
public Set<String> findLongTracks(List<Album> albums){
Set<String> tracksNames=new HashSet<>();
for(Album album:albums){
for(Track track:album.getTrackList()){
if(track.getLength()>60){
String name=track.getName();
trackNames.add(name);
}
}
}
return trackNames;
}
重构第一步
public Set<String> findLongTracks(List<Album> albums){
Set<String> tracksNames=new HashSet<>();
//对象中定义的getTracks方法会返回一个Stream对象
albums.stream()
.forEach(album->{
album.getTracks()
.forEach(track->{
if(track.getLength()>60){
trackNames.add(track.getNames)
}
});//这里为什么需要加分号?
});
return trackNames;
}
重构第二步
public Set<String> findLongTracks(List<Album> albums){
Set<String> tracksNames=new HashSet<>();
albums.stream()
.forEach(album->{
album.getTracks()
.filter(track->track.getLength()>60)
.map(track->track.getName())
.forEach(name->trackNames.add(name));
});
return trackNames;
}
重构第三步
public Set<String> findLongTracks(List<Album> albums){
Set<String> tracksNames=new HashSet<>();
//将多个Stream合并为一个
albums.stream()
.flatMap(album->album.getTracks()).
.filter(track->track.getLength()>60)
.map(track->track.getName())
.forEach(name->trackNames.add(name));
return trackNames;
}
重构第四步
public Set<String> findLongTracks(List<Album> albums){
return albums.stream()
.flatMap(album->album.getTracks()).
.filter(track->track.getLength()>60)
.map(track->track.getName())
.collect(toSet());
}
Lambda表达式的宗旨是获取值而不是变量!所以尽量不要产生垃圾变量来保存中间结果。
书上的一段描述:Lambda表达式描述了数据上的操作,明确了要达成什么转化,而不是说明如何转化。这种方式写出的代码,存在的缺陷更少。
怎样理解这段话呢?
这说明一段安全的程序应该着重于从原始数据转化成结果,而不在于过程,并且不要把数据转化过程暴露出来(即产生一系列中间变量)也就是不必说明数据是如何转化的,只要最终达成转化即可!
明确要达成什么转化,而不是说明如何转化的另外一层含义在于写出的函数没有副作用!(没有副作用的函数不会改变程序或外界的状态)
因此无论何时,将Lambda表达式传给Stream上的高阶函数,都应该尽量避免副作用。唯一的例外是forEach方法,他是一个终结方法。