为什么要有练习题?

   所谓学而不思则罔,思而不学则殆,在系列第一篇就表明我认为写博客,既是分享,也是自己的巩固,我深信"纸上得来终觉浅,绝知此事要躬行"的道理,因此之后的几篇博客都会在知识点后面附上几道练习题,不会单独开一篇来说练习题。

   大部分题练习题来自于Richard Warburton《Java8 函数式编程》,练习题的难度不会很难,大部分都十分的基础(可能偶尔会有一两道进阶题),并且我在后面也会附上可供参考的思路与代码,但是我认为想要学习的人(包括我)应当首先对问题沉下心来思考,然后再查看参考答案,当然如果看博客的你没有这么多时间我的建议是记录下来,闲暇时光在脑袋里想一想也是好的,我认为鲁迅的说的时间就像海绵,挤挤就有了是十分有道理的,其实大家都没有自己想象的那么忙。

基础题

Question 1 (常用流操作)

  • 编写一个求和函数,计算流中所有数的和。例int addUp(Stream numbers)
  • 编写一个函数,参数为艺术家集合,返回一个字符串集合,其中包含了艺术家的姓名与国籍。(艺术家类名为Artist,里面有获得姓名与国籍的get方法getName()与getNationality())
  • 编写一个函数,参数为专辑集合,返回一个由 最多包含3首歌曲的专辑 组成的集合。(专辑类名为Album,里面包含了一个返回本专辑所有歌曲的集合的方法getTrackList())

请耐心思考一会,看看能不能自己写出来,下面给一些参考的思路,具体的方法确实是有很多种的。


Question 1 的参考思路

  • 编写一个求和函数,计算流中所有数的和。

    这道练习题要求计算流中所有的数的和,参数为流,返回他们的和,这其实就是要求我们重复实现类库中的sum()方法,那系列第二篇提到的reduce操作里面已经提到了,类库中的sum,max,min等一系列操作都是用reduce完成的,因此在这里我们也可以用reduce进行操作。参考代码如下
    public static int addUp(Stream<Integer> numbers){
reutrn numbres.reduce(0,(x,y) -> x+y);
}
  • 编写一个函数,参数为艺术家集合,返回一个字符串集合,其中包含了艺术家的姓名与国籍。

    这道题参数为艺术家集合,返回值为字符串,那么显然这是涉及到映射map的操作,但是要将一个艺术家元素映射成两个字符串(分别是这个艺术家的姓名与这个艺术家的国籍),那么就要涉及到flatMap了,flatMap方法可以用Stream替换值,然后在将多个Stream链接成一个Stream,对于本题来说,使用艺术家姓名与艺术家国籍的流替换掉艺术家,再将这些流链接到一起即可。

    更具体一点的说法,假设有2个艺术家,分别是来自中国的张三与来自美国的jack,那么直接使用map映射返回的结果是2个流,格式大约为这样:[张三,中国],[jack,美国],使用flatMap就可以将这两个流合并,于是格式就变成了[张三,中国,jack,美国],符合题目中的返回一个字符串集合。

    参考代码如下
    public static List<String> getArtistNamesAndNations(List<Artist> artists){
return artists.stream()
.flatMap(artist -> Stream.of(artist.getName(),artist.getNationality()))
.collect(toList());
}

注:这里最后一行要说明一下,事实上标准的写法应该是collect(Collectors.toList()),但是因为这个方法十分常用,所以在类开头导入了静态方法

import static java.util.stream.Collectors.toList

这是来自于java5的特性,可以方便静态属性与方法的调用。

  • 编写一个函数,参数为专辑集合,返回一个由 最多包含3首歌曲的专辑 组成的集合。(专辑类名为Album,里面包含了一个返回本专辑所有歌曲的集合的方法getTrackList())

    这道题很明显参数为专辑,返回值也是专辑,中间多了一个条件,因此使用过滤器即可。参考代码如下
    public static List<Album> getAlbumsWithMostThreeTracks(List<Album> albums){
return albums.stream()
.filter(album -> album.getTrackList().size <= 3)
.collect(toList());
}

Question 2 (关于迭代)

  • 修改下面的代码,将外部迭代转换成内部迭代
  • 为了保证不理解错误,说明一下,艺术家有的是个人,有的是团体或者乐队,getMembers()方法是获得该艺术家团体的所有人数,如果是个人艺术家,人数自然就是一个人。
  • 并且注意下面这段代码的最后一行有效行,members使用了count()方法来计数,因此该方法的返回值是一个stream流,想要获得数量要用count()方法。
    int totalMembers = 0;
for(Artist artist: artists){
Stream<Artist> members = artist.getMembers();
totalMembers += members.count();
}

附:getMembers方法

    public Stream<Artist> getMembers() {
return members.stream();
}

Question 2 的参考思路

不拘泥于具体实现方式,首先了解清楚这段代码想做什么,有几步。这段代码想做的就是统计出所有艺术家的人数,因为有的艺术家是团体,有的艺术家是个人,个人的艺术家就算一个人,团体的话就统计出该团体的所有人数。那么相应的步骤就显而易见了。

第一步统计出每个艺术家个人或者团体的人数,第二步将他们求和。对于流操作第一步是一个惰性求值,首先将艺术家映射成他们的团体人数,第二步是一个及早求值,对这些人数进行求和。

参考代码如下

public static int countAllArtistMembers(List<Aritst> artists){
return artists.stream()
.map(artist -> artist.getMembers().count())
.reduce(0,Integer::sum);
}

当然目前还没有介绍一些属于高阶流的例如收集器等,事实上下面这段使用数值流的代码看起来更舒服一些,阅读性也更强

public static int countAllArtistMembers(List<Aritst> artists){
return artists.stream()
.mapToInt(artist -> artist.getMembers().count())//统计人数,转换成数值流
.sum();//对人数求和

Question 3 (判断求值方式)

-依据以下两个方法的方法签名,判断下面两个方法是属于惰性求值还是及早求值

1.boolean anyMatch(Predicate<? super T> predicate);

2.Stream limit(long maxSize);

注:第一题的Predicate是java8的函数接口,看不明白并没有关系,后面会进行介绍,题目与之也没有什么联系。


Question 3 的参考思路

之前系列博客上一篇将惰性求值与及早求值的区别已经介绍过,这两者最大的区别的就是惰性求值返回的值是Stream,而及早求值是直接返回具体的数据,那么结果就显而易见了

第一个方法返回了具体的boolean数值,第二个方法返回的则是Steam流,因此第一个方法属于及早求值,第二个方法属于惰性求值。


Question 4 (字符串练习题两道)

  • 计算一个字符串中小写字母的个数

    注:可以参考Java8 string中新引入的chars方法

  • 在一个字符串集合中,找出包含最多小写字母的字符串。

对于还没有熟悉流操作的同学来说,第一反应思路是for循环,if语句之类的,其实这很正常,那么不妨试着先写出你第一反应程序的伪代码,接着再使用流操作对它进行重构,接着再想一想重构的过程中,提取出真正的几步需求,也就是我要干什么,总共有几步,分解成流操作可以怎么样,是过滤器?还是收集器?又还是映射?还是比较器?,慢慢的这样思考,相信以后工作中碰到相应的问题第一反应就会是流操作啦:)


Question 4 的参考思路

  • 计算一个字符串中小写字母的个数

    首先引入一段java8 String类的chars方法的api
    /**
* Returns a stream of {@code int} zero-extending the {@code char} values
* from this sequence. Any char which maps to a <a
* href="{@docRoot}/java/lang/Character.html#unicode">surrogate code
* point</a> is passed through uninterpreted.
*
* <p>If the sequence is mutated while the stream is being read, the
* result is undefined.
*
* @return an IntStream of char values from this sequence
* @since 1.8
*/
public default IntStream chars()

简单点说呢,这个方法返回一个由string里字符组成的数值流,数值流,当然也是属于流的一种了。

那么这个问题的思路是这样的,第一步获得所有字母的流,这点通过chars方法已经得到了,第二步选出所有的小写字母,这点的话使用过滤器filter也可以轻松做到(也就是第一反应的if),第三部统计个数,这个就不用说了吧,count()方法搞定。

下面是参考代码

    public class Question4 {
public static int countStringLowercaseLetters(String string){
return string.chars()//获得字母流
.filter(Character::isLowerCase)//筛选出所有小写字母
.count;//统计数量
}
}

这里注意到参考代码里给了一个类,那是因为后面一道题要用到这个方法,为了减少代码量,这里给一个类,方便到时候引用。

  • 在一个字符串集合中,找出包含最多小写字母的字符串。

    同样的理清思路,第一步统计出每一个字符串小写字母的数量(这一步上一题已经做完了)并将他们映射成数值流,第二步选出这些数值中最大的。

    这里依旧给两个参考代码,一个是使用了Comparator比较器,返回的是optional对象,使用get()方法获得里面的值,另一个是直接映射成数值流(个人推荐第二种啦)

  • 使用比较器

    public static Optional<String> mostLowcaseLetters(List<String> strings){
return strings.stream()
.max(Comparator.comparing(Question4::countStringLowercaseLetters));
}
  • 使用数值流
    public static int mostLowcaseLetters(List<String> strings){
return strings.stream()
.mapToInt(Question4::countStringLowercaseLetters))
.max();
}

思考题

  • 试着只用reduce和Lambda表达式写出实现Stream上的filter操作的代码
  • 试着只用reduce和Lambda表达式写出实现Stream上的map操作的代码

注:使用collect收集器会更容易一些,不过目前本系列博客还没有介绍。当然只是用reduce与lambda也是完全可以做到的,可以试着看一看java8 filter与map的源码。

思考题答案放在下一篇博客里,当然实现的方式有很多,这里所有的答案都只是一个参考,仅提供一种思路。

本篇小结

  本篇是属于上一篇的一个延伸,由几道练习题讲解流的一些实际操作与思考过程,在这里还是要重复说一下上面的重构建议,可能大家(包括我一开始)刚开始想当然就是for与if之类,确实很容易这样,我的建议就是试着用流操作去重构自己的原先代码,同时从具体的实现细节中脱离出来,去关注究竟需要做什么,这样你会发现解决一个问题并没有想象的那么难。

谈一谈Java8的函数式编程 (三) --几道关于流的练习题的更多相关文章

  1. 谈一谈Java8的函数式编程(二) --Java8中的流

    流与集合    众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...

  2. 快速掌握Java8 Stream函数式编程技巧

    函数式编程优势 "函数第一位",即函数可以出现在任何地方. 可以把函数作为参数传递给另一个函数,还可以将函数作为返回值. 让代码的逻辑更清晰更优雅. 减少了可变量(Immutabl ...

  3. Java8自定义函数式编程接口和便捷的引用类的构造器及方法

    什么是函数编程接口? 约束:抽象方法有且只有一个,即不能有多个抽象方法,在接口中覆写Object类中的public方法(如equal),不算是函数式接口的方法. 被@FunctionalInterfa ...

  4. Java8函数之旅 (三) --几道关于流的练习题

    为什么要有练习题?    所谓学而不思则罔,思而不学则殆,在系列第一篇就表明我认为写博客,既是分享,也是自己的巩固,我深信"纸上得来终觉浅,绝知此事要躬行"的道理,因此之后的几篇博 ...

  5. java8 lambda 函数式编程

    package com.atguigu.java8; import java.util.ArrayList; import java.util.Comparator; import java.util ...

  6. js函数式编程(三)-compose和pointFree

    compose即函数嵌套组合 组合compose在第一篇已经初见端倪,可以感受一下.compose函数的实现用闭包的方法.不完善实现如下: const compose = (f, g) => { ...

  7. [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念

      本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程?   java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...

  8. Java8函数式编程探秘

    引子 将行为作为数据传递 怎样在一行代码里同时计算一个列表的和.最大值.最小值.平均值.元素个数.奇偶分组.指数.排序呢? 答案是思维反转!将行为作为数据传递. 文艺青年的代码如下所示: public ...

  9. [译]Java8:循环与函数式编程

    Java8函数式编程的加入彻底改变了游戏规则.对Java开发者来说这是一个全新的世界,我们也需要做出相应的改变. 在这篇文章中我们将找寻传统循环代码的可替代方案.Java8的函数式编程特性改变了编程思 ...

随机推荐

  1. linux环境下安装jdk(本文示例是jdk1.6.0_export JAVA_HOME=/usr/java/jdk1.6.0_45 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar45)

    第一步:创建一个文件夹安装jdk(虽说地址一般自定义,但是为了方便查找请按照笔者建议目录 ):/usr/java 将jdk-6u45-linux-x64.bin文件放到   /usr/java 文件夹 ...

  2. [UWP]了解模板化控件(5):VisualState

    1. 功能需求 使用TemplatePart实现上篇文章的两个需求(Header为空时隐藏HeaderContentPresenter,鼠标没有放在控件上时HeaderContentPresent半透 ...

  3. web前端概念巩固(一)

    h5: 1.web语义化 Web语义化是指在进行HTML结构.表现.行为设计时,尽量使用语义化的标签,使程序代码简介明了,易于进行Web操作和网站SEO,方便团队协作的一种标准,以图实现一种" ...

  4. Java将一个目录下的所有数据复制到另一个目录下

    /* 将"C:\\JavaProducts\\Source"下的所有数据复制到"C:\\Target"下 */ import java.io.*; public ...

  5. 基于bootstrap的bootstrap-editable插件实现即时编辑功能

    1.引用基本css和js: <link href="bootstrap3/css/bootstrap.min.css" rel="stylesheet" ...

  6. 推送一个已有的代码到新的 gerrit 服务器

    1.指定项目代码库中迭代列出全部ProductList(.git)到pro.log文件中 repo forall -c 'echo $REPO_PROJECT' | tee pro.log pro.l ...

  7. C++STL vector详解(杂谈)

    介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用.通 ...

  8. CSS垂直和水平居中

    在css中,居中使用十分频繁. 居中分为水平和垂直居中 水平居中十分简单: body{ background:#f90; } body统一为这个颜色 div { margin:0 auto; back ...

  9. webUI自动化测试框架---”pyswat“介绍

    webUI自动化测试框架---"pyswat"介绍 大家好我是lamecho 辣么丑,今天给大家介绍一款web自动化测试框架pyswat.  "pyswat"是 ...

  10. Facebook 宣布开源Python重写后的OnlineSchemaChange

    本文会简要介绍,OnlineSchemaChange在经历从PHP到Python重写后的改进和变化 文章欢迎转载,但转载时请保留本段文字,并置于文章的顶部 作者:卢钧轶(cenalulu) 本文原文地 ...