/**
* @author zhangdi
* @description 常用的流操作
*/
public class LambdaChapter3_3 {
public static void main(String[] args) {
// new LambdaChapter3_3().testCollect();
// new LambdaChapter3_3().testMap_usingMap();
// new LambdaChapter3_3().testfilter_usingLambda();
// new LambdaChapter3_3().testflatMap();
new LambdaChapter3_3().testMaxAndMin();
}
// 3.3.1 collect(toList()) :collect(toList()) 方法由 Stream 里的值生成一个列表,
// 是一个及早求值操作。
public void testCollect() {
List<String> collected = Stream.of("a", "b", "c") // <1>
.collect(Collectors.toList()); // <2>
assertEquals(Arrays.asList("a", "b", "c"), collected); // <3>
}
// 3.3.2 map 如果有一个函数可以将一种类型的值转换成另外一种类型, map 操作就可以使用该函数, 将一个流中的值转换成一个新的流
/**
* 使用 for 循环将字符串转换为大写
*/
public void testMap_usingFor() {
List<String> collected = new ArrayList<>();
for (String string : asList("a", "b", "hello")) {
String upperCase = string.toUpperCase();
collected.add(upperCase);
}
assertEquals(asList("A", "B", "HELLO"), collected);
}
/**
* 使用 map 循环将字符串转换为大写
*/
public void testMap_usingMap() {
List<String> collected = Stream.of("a", "b", "hello").map(string -> string.toUpperCase())
.collect(Collectors.toList());
}
// 3.3.3 filter ;遍历数据并检查其中的元素时, 可尝试使用 Stream 中提供的新方法 filter
/**
* 使用循环遍历列表, 使用条件语句做判断
*/
public void testfilter_usingFor() {
List<String> beginningWithNumbers = new ArrayList<>();
for (String value : asList("Demacia", "Bilgewater", "2Valoran")) {
if (isDigit(value.charAt(0))) {
beginningWithNumbers.add(value);
}
}
assertEquals(asList("2Valoran"), beginningWithNumbers);
}
/**
* 函数式风格
*/
public void testfilter_usingLambda() {
List<String> beginningWithNumbers = Stream.of("Demacia", "Bilgewater", "2Valoran")
.filter((value) -> isDigit(value.charAt(0))).collect(toList());
assertEquals(asList("2Valoran"), beginningWithNumbers);
}
// 3.3.4 flatMap: flatMap 方法可用 Stream 替换值, 然后将多个 Stream 连接成一个 Stream
// 前面已介绍过 map 操作, 它可用一个新的值代替 Stream 中的值。 但有时, 用户希望让 map
// 操作有点变化, 生成一个新的 Stream 对象取而代之。
/**
* Question:假设有一个包含多个列表的流, 现在希望得到所有数字的序列。
* 包含多个列表的 Stream
*/
public void testflatMap() {
List<Integer> together = Stream.of(asList(1, 2), asList(3, 6)).flatMap(value -> value.stream())
.collect(toList());
assertEquals(asList(1, 2, 3, 6), together);
}
// 3.3.5 max和min:Stream 上常用的操作之一是求最大值和最小值
/**
* 使用 Stream 查找最短曲目
*/
public void testMaxAndMin() {
List<Track> tracks = asList(new Track("Bakai", 524), new Track("Violets for Your Furs", 378),
new Track("Time Was", 451));
Track shortestTrack = tracks.stream().min(Comparator.comparing(track -> track.getLength())).get();
/**这种new Track("Violets for Your Furs", 378)的方式会报错:Exception in thread "main" java.lang.AssertionError:
expected:<testLambda.LambdaChapter3_3$Track@5b8540fd> but was:<testLambda.LambdaChapter3_3$Track@f6dff17f>
*/
// assertEquals(new Track("Violets for Your Furs", 378) ,shortestTrack);
assertEquals(tracks.get(1), shortestTrack);
}
// 3.3.6 通用模式
/**
* 使用 for 循环查找最短曲目
*/
public void testMaxAndMin_usingFor() {
List<Track> tracks = asList(new Track("Bakai", 524), new Track("Violets for Your Furs", 378),
new Track("Time Was", 451));
Track shortestTrack = tracks.get(0);// 假定第一个track为最短曲目
for (Track track : tracks) {
if (track.getLength() < shortestTrack.getLength()) {
shortestTrack = track;
}
}
assertEquals(tracks.get(1), shortestTrack);
}
// 例 3-15 中的伪代码体现了通用模式的特点:reduce 模式 例3-15
public void forkCode() {
// Object accumulator = initialValue;
// for (Object element : collection) {
// accumulator = combine(accumulator, element);
// }
}
// 3.3.7 reduce:reduce 操作可以实现从一组值中生成一个值。 在上述例子中用到的 count、 min 和 max 方法,
// 因为常用而被纳入标准库中。 事实上, 这些方法都是 reduce 操作。
/**
* 使用 reduce(reduce的类型是BinaryOperator) 求和:将两个参数相加, acc 是累加器, 保存着当前的累加结果
*/
public void testSum_usingReduce() {
int count = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
assertEquals(6, count);
}
// 例 3-17 展开 reduce 操作
public void testReduce_expand_To_Sum() {
BinaryOperator<Integer> accumulator = (acc, element) -> acc + element;
int count = accumulator.apply(accumulator.apply(accumulator.apply(0, 1), 2), 3);
}
// 例 3-18 使用命令式编程方式求和 :在命令式编程方式下, 每一次循环将集合中的元素和累加器相加, 用相加后的结果更新累加器的值。
public void testLambda_To_Sum() {
int acc = 0;
for (Integer element : asList(1, 2, 3)) {
acc = acc + element;
String str = "1";
}
assertEquals(6, acc);
}
// 3.3.8 整合操作 :如何将问题分解为简单的 Stream 操作
// 第一个要解决的问题是, 找出某张专辑上所有乐队的国籍。
/*
* 问题步骤分解: 1. 找出专辑上的所有表演者。 2. 分辨出哪些表演者是乐队。 3. 找出每个乐队的国籍。 4. 将找出的国籍放入一个集合。
*/
@Test
public void findnNationality() {
// Album album2 = SampleData.manyTrackAlbum;
Album album = new Album("album_test",
new ArrayList<Track>(Arrays.asList(new Track("track", 2), new Track("track2", 3))),
new ArrayList<Artist>(Arrays.asList(new Artist("the tester", "china"), new Artist("haha", "USA")))
);
Set<String> set = album.getMusicians().filter((artist) -> artist.getName().startsWith("the"))// 假设所有乐队都以"the"开头.
.map(artist -> artist.getNationality()).collect(Collectors.toSet());
for (String string : set) {
System.out.println("set:" + string);
}
// System.out.println("set:"+set.toArray().toString());
}
// 3.4 重构遗留代码
// 例 3-19 遗留代码: 找出长度大于 1 分钟的曲目 (假定选定一组专辑, 找出其中所有长度大于 1 分钟的曲目名称。)
public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
for (Album album : albums) {
for (Track track : album.getTrackList()) {
if (track.getLength() > 60) {
String name = track.getName();
trackNames.add(name);
}
}
}
return trackNames;
}
// 例 3-20 重构的第一步: 找出长度大于 1 分钟的曲目
public Set<String> findLongTracks_lambda_step1(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
// 使用 Stream 的 forEach 方法替换掉 for 循环
albums.stream().forEach(album -> {
album.getTracks().forEach(track -> {
if (track.getLength() > 60) {
trackNames.add(track.getName());
}
});
});
return trackNames;
}
// 例 3-21 重构的第二步
public Set<String> findLongTracks_lambda_step2(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
albums.stream().forEach(album -> {
// 最内层的 forEach 方法有三个功用: 找出长度大于 1 分钟的曲目, 得到符合条件的曲目名称, 将曲目名称加入集合 Set
album.getTracks().filter(track -> track.getLength() > 60).map(track -> track.getName())
.forEach(name -> trackNames.add(name));
});
return trackNames;
}
// 例 3-22 重构的第三步: 找出长度大于 1 分钟的曲目
public Set<String> findLongTrackslambda_step3(List<Album> albums) {
Set<String> trackNames = new HashSet<>();
// 任何时候想转化或替代代码, 都该使用 map 操作。 这里将使用比 map 更复杂的 flatMap 操作, 把多个
// Stream 合并成一个 Stream 并返回。
albums.stream().flatMap(album -> album.getTracks()).filter(track -> track.getLength() > 60)
.map(track -> track.getName()).forEach(name -> trackNames.add(name));
return trackNames;
}
// 例 3-23 重构的第四步: 找出长度大于 1 分钟的曲目
public Set<String> findLongTrackslambda_step4(List<Album> albums) {
return albums.stream()
.flatMap(album->album.getTracks())
.filter(track->track.getLength()>60)
.map(track->track.getName())
.collect(Collectors.toSet());
}
//3.6 高阶函数:高阶函数是指接受另外一个函数作为参数, 或返回一个函数的函数。
// 3.8 要点回顾
// Ŗ 内部迭代将更多控制权交给了集合类。
// Ŗ 和 Iterator 类似, Stream 是一种内部迭代方式。
// Ŗ 将 Lambda 表达式和 Stream 上的方法结合起来, 可以完成很多常见的集合操作。
java8函数式编程(2)
最新推荐文章于 2020-02-28 15:16:37 发布