通过前面的例子,相信你已经对Lambda表达式有了一个初步了解了,前一篇主要是属性Lambda的语法,这篇我们继续。

        Java8做了很多改进,以便我们少写点代码。接下来我们就来稍微了解一下,JDK自带的库文件中的一些简便工具吧。在本系列第〇篇,我就以一段“滑稽”的代码开头。说,在集合里面经常会做这种“滑稽”的处理。幸运的是,Java8给集合类提供了新技能,解决这个问题,新技能是Streams。我们现在就来了解他。

        假设我们现在要统计一个List<Person>里面的男性个数,那么代码我们通常是这样写的。



[java]​ 

​view plain​

 ​​copy​


  1. public int countMan(){
  2. int count = 0;
  3. Iterator<Person> iterator = personList.iterator();
  4. while(iterator.hasNext()){
  5. Person person = iterator.next();
  6. if(person.getGender().equal(“男”)){
  7. Count++;
  8. }
  9. }
  10. return count;
  11. }



他的处理过程如下图所示


Stream Java8的集合类利器——走进Java Lambda(二)_java


那么在Java8中,我们可以把上图的处理过程变成,下图这样子



Stream Java8的集合类利器——走进Java Lambda(二)_java_02


那么代码怎么写呢,可以写成这样:

Stream.filter()和Stream.count()


[java]​ 

​view plain​

 ​​copy​


  1. public int countMan(){
  2. return personList.stream().filter(person -> person.getGender().equal(“男”)).count();
  3. }


Stream在这里主要做了两件事情。

        第一、把所有人获取到

        第二、把“男性”过滤出来。

        我们最后调用count()就是获取结果,相当于获取新的List的size。只是相当于,仅仅为了利于我们理解方便,实际上,Stream并没有new一个新List。我们要做的事情就是,获取到Stream,告诉Stream我们要做什么,然后问Stream要结果。那要是我们不问Stream要结果,那会怎么样,答案是Stream绝对什么都不做。下面这段代码等于没有写:


[java]​ 

​view plain​

 ​​copy​


  1. public void function(){
  2. personList.stream().filter(person -> person.getGender().equal(“男”));
  3. }


你若不信,可以用下面一段代码​​测试​​。


[java]​ 

​view plain​

 ​​copy​


  1. public void function(){
  2. personList.stream().filter(person -> {
  3. System.out.println(“Go!”);
  4. person.getGender().equal(“男”);
  5. });
  6. }


        你会发现,其实他什么都没有打印,只需在后面向Stream要结果(比如调用.count()方法),那么你一定会看到输出了“​​Go​​!”。是不是很像延迟加载?Bingo!他更像是设计模式里面的“生成器模式”不是吗。等等,Stream支持延迟加载可以支持“预先加载”(Eager)吗?不能我不会BB,count()方法就是。比如现在我们要获取personList里面年龄大于7的人这样一个子集,怎么弄呢?调用collect(toList)方法。如下:


[java]​ 

​view plain​

 ​​copy​


  1. public void function(){
  2. List<Person> personSublist = personList.stream().filter(person -> person.getAge() > 7).collect(Collectors.toList());
  3. System.out.println(personSublist);
  4. }


这样personSublist绝逼不是null。前面刚说[不问Stream要结果,Stream绝对不会听你的要求]。这里我们向Stream要结果了吗?要了,Collectors.toList()就相当于说“嘿,把结果个我打包到新List里面”。哦,敢情这个“预先加载”是幻觉?也不能这么说,我们得看看调用的方法返回的是什么值。若Stream的方法返回的是Stream类型,那么就是延迟加载,否则不是。

Stream.map()

讲了filter我们再来看看会用得很多的map,(你若知道​JavaScript​里面Array的filter和map,可以比较一下,不知道也没关系)。我们先假设有如下这么一个List。


[java]​ 

​view plain​

 ​​copy​


  1. List<String> collected = new ArrayList<>();
  2. collected.add("alpha");
  3. collected.add("beta");
  4. collected.add("cool");
  5. collected.add("delta");


我们需要把里面的字母全部转换成大写,在Java8里,我们可以利用Steam这样写代码。


[java]​ 

​view plain​

 ​​copy​


  1. public void function(){
  2. List<String> collected = new ArrayList<>();
  3. collected.add("alpha");
  4. collected.add("beta");
  5. collected.add("cool");
  6. collected.add("delta");
  7. collected = collected.stream().map(string -> string.toUpperCase())
  8. .collect(Collectors.toList());
  9. }


map的意思是,1对1转换。也就是说map方法执行后,得到的“新集合”跟原集合的size是一样大的,而filter方法执行后得到的“子集合”可以比原集合的size小。

Map和filter的区别可以用下面图示来表示



Stream Java8的集合类利器——走进Java Lambda(二)_java_03    

Stream Java8的集合类利器——走进Java Lambda(二)_加载_04


我们用map来干什么,比如前面说过,对List<Integer>进行处理的时,会频繁地包装和解包。这样消耗内存占用时钟。我们可以用map转换成List<int>类型再计算。关于这方面的,放在后面篇章里面说。

Stream.flatMap()

除了这两个常用的需求外还有一个用于合并用的方法叫做flatMap,我们看看下面代码,使用flatMap的一个例子。



[java]​ 

​view plain​

 ​​copy​


  1. /**
  2. * 测试flatMap类似于addAll的功能
  3. */
  4. @Test
  5. public void flatMapTest() {
  6. List<Integer> collected0 = new ArrayList<>();
  7. collected0.add(1);
  8. collected0.add(3);
  9. collected0.add(5);
  10. List<Integer> collected1 = new ArrayList<>();
  11. collected1.add(2);
  12. collected1.add(4);
  13. collected1 = Stream.of(collected0, collected1)
  14. .flatMap(num -> num.stream()).collect(Collectors.toList());
  15. System.out.println(collected1);// 1,3,5,2,4
  16. }



或许有人会问List里面有有addAll,为什么还需要flatMap这个玩意呢。答案是这样的,flatMap可以一下子合并多个集合,并且,不会正真意义上new出新的集合出来,那么明显会占用更少的内存空间,若你要的就是合并后的新集合,那另当别论。这都是小儿科,若你想把List<Person>里面的Person里面的name和age单独放入一个集合内,这时选择flatMap来完成,也是极好的。

Stream.reduce()

介绍了过滤,转换,合并之后,再看一个,遍历操作临近元素的例子,比如现在有个放有整数的List,我们要得到其总和,那么我们可以用下面的代码来实现:


[java]​ 

​view plain​

 ​​copy​


  1. /**
  2. * 测试reduce方法
  3. */
  4. @Test
  5. public void reduceTest() {
  6. int sumAll = Stream.of(1, 2, 3, 4).reduce(0,
  7. (sum, element) -> sum + element);// 给一个0是用来启动,的,若给-1,结果会是9
  8. System.out.println(sumAll);// 10
  9. }


上面代码运行的逻辑如下图所示


Stream Java8的集合类利器——走进Java Lambda(二)_延迟加载_05


讲到这里,就顺便提及一下​​Java​​双冒号运算,对于C++的同学来说,这种运算符并不陌生,但是Java到目前才引用进来。对于上面例子的代码我们还可以写成这样。



[java]​ 

​view plain​

 ​​copy​


  1. @Test
  2. public void reduceTest() {
  3. int sumAll = Stream.of(1, 2, 3, 4).reduce(0,
  4. Integer::sum);//  Integer在Java8中提供了sum求和静态方法。

[java]​ 

​view plain​

 ​​copy​


  1. System.out.println(sumAll);// 10



这里只是提及,先不做细讲。

Stream.mapToInt()

在Java8里面我们可以,借助Stream对集合类进行统计。比如,我们要对一群人的年龄信息进行简单统计,那么我们可以这样写



[java]​ 

​view plain​

 ​​copy​


  1. @Test
  2. public void mapToIntTest(){
  3. List<Person> persons =  new PersonFactory().getPersons(8);//随机获取8个person实例
  4. IntSummaryStatistics intSummaryStatistics = persons.stream().mapToInt(person-> person.getAge()).summaryStatistics();
  5. System.out.println("最大年龄:"+intSummaryStatistics.getMax()); //最大值
  6. System.out.println("最小年龄:"+intSummaryStatistics.getMin()); //最小值
  7. System.out.println("年龄总和:"+intSummaryStatistics.getSum()); //总计
  8. System.out.println("人数:"+intSummaryStatistics.getCount());   //个数
  9. System.out.println("平均年龄:"+intSummaryStatistics.getAverage());//平均数返回的是double类型
  10. }


注意,这里我们使用了mapToT方法,T表示基础数据类型,包括int long double,注意没有float哦。mapToT方法返回值是TStream类型,TStream类包含了一些处理基础数据的方法,可以让我们更方便。我们使用mapToT的原因,不仅仅是方便,还在于性能。我们知道,因为泛型的原因,可以有List<Integer>但是不能有List<int>,这里的IntStream就相当于是List<int>,int 所占内存比Integer小。根据上面两个例子,相信你能够自己推测出​flatMapToInt()​这种flatMapToT()的用法和功能了吧,这里的T同样表示int long double基础数据类型。若你还是对MapToT方法感到困惑,不知道怎么来的,那么,接下来我们就单独讲讲上面代码里Stream.mapToInt(person -> person.getAge())这一段,这里大有乾坤哦。Stream对mapToInt 的定义是这样的:



[java]​ 

​view plain​

 ​​copy​


  1. IntStream mapToInt(ToIntFunction<? super T> mapper);

参数是 ​ToIntFunction​,这是个神马?ToIntFunction是一个函数接口,它只有一个方法。他的源码是这样写的:





[java]​ 

​view plain​

 ​​copy​


  1. @FunctionalInterface
  2. public interface ToIntFunction<T> {
  3. /**
  4. * Applies this function to the given argument.
  5. *
  6. * @param value the function argument
  7. * @return the function result
  8. */
  9. int applyAsInt(T value);
  10. }

意思是说,这个接口可以调用applyAsInt方法,把任意一个对象T转换成int基础数据类型。那么这段代码:




[java]​ 

​view plain​

 ​​copy​


  1. Stream.mapToInt(person -> person.getAge())

就是一个省略了ToIntFunction的匿名函数接口,也就相当于是:




[java]​ 

​view plain​

 ​​copy​


  1. ToIntFunction<Person> trans = person-> person.getAge();
  2. Stream.mapToInt(trans)

同时也相当于是代码: (纯粹是为了复习)


[java]​ 

​view plain​

 ​​copy​


  1. ToIntFunction<Person> trans = new ToIntFunction<Person>() {
  2. @Override
  3. public int applyAsInt(Person person) {
  4. return person.getAge();
  5. }
  6. };
  7. Stream.mapToInt(trans);

除了 ​ToIntFunction​还有

ToLongFunction 、ToDoubleFunction​,它们的目的是把任意对象转换成基础数据类型。能不能把基本类型反转换成其他对象,能!但是得用别的函数接口,如

IntFunction、 LongFunction、 DoubleFunction​。更多功能请参考Java的API。




现在我们已经讲过几种Stream里面的方法了,Stream还有很多方法,这里就不一一介绍了,下一篇再说。末了要说的是,Stream可以链式调用,可以把链条拉得很长。比如,我们要获取,年龄在加上5岁后仍然小于8岁的男性,的姓氏。我们可以用类似下面的代码来实现



[java]​ 

​view plain​

 ​​copy​


  1. @Test
  2. public void longChainTest() {
  3. List<Person> personList = new PersonFactory().getPersons(8);//随机获取8个person实例
  4. List<String> names = new ArrayList<>(); personList.stream().map(person -> { person.setAge(person.getAge() + 5);//年龄加5岁
  5. return person; })
  6. .filter(person -> person.getAge() < 8)//年龄小于8
  7. .filter(person -> person.getGen().equals("男"))//男性
  8. .forEach(person -> names.add(person.getFirstName()));//获取姓氏
  9. System.out.println(names);}




我们先告一段落吧,要是没搞清楚,可以再看一遍上面的代码。

若你已经清楚了,那么小二我就上问题了。

1.Stream类是不是函数接口,为什么

2.以下是Stream的方法大纲的一部分(分号后表示返回类型),指出这些方法是延迟加载,还是超前加载(eager也有预先加载,贪婪加载的叫法)

sorted():Stream<T>

toArray():Object[]

skip(long):Stream<T>

(载加迟延是不toArray有只)

3.如何利用Stream来实现,将personList的姓氏和性别组合成新的List?

参考答案:


[java]​ 

​view plain​

 ​​copy​


  1. @Test
  2. public void ansTest() {
  3. List<Person> personList = new PersonFactory().getPersons(5);<span style="font-family: Arial, Helvetica, sans-serif;">//随机生成5个person实例
  4. List<String> names = personList
  5. .stream()
  6. .flatMap( person -> Stream.of(person.getFirstName(),
  7. person.getGen())).collect(Collectors.toList());
  8. System.out.println(names);
  9. }


4.运行和检查下面两段代码,并思考里作为无副作用的编程,我们应该提倡哪种方式?


[java]​ 

​view plain​

 ​​copy​


  1. @Test
  2. public void convertTest() {
  3. List<String> collected = new ArrayList<>();
  4. collected.add("alpha");
  5. collected.add("beta");
  6. collected.add("cool");
  7. collected.add("delta");
  8. collected.stream().map(string -> string.toUpperCase())
  9. .count();
  10. System.out.println(collected);//此处打印出来的是大写还是小写,为什么?
  11. }
  12. @Test
  13. public void longChainTest() {
  14. List<Person> personList = new PersonFactory().getPersons(5);//随机生成5个person实例
  15. List<Integer> ages = new ArrayList<>();
  16. personList.stream().forEach(person -> ages.add(person.getAge()));
  17. System.out.println(ages);//第一处打印
  18. ages.clear();
  19. personList.stream().map(person -> {
  20. person.setAge(person.getAge() + 10);
  21. return person;
  22. }).count();
  23. personList.stream().forEach(person -> ages.add(person.getAge()));
  24. System.out.println(ages);//第二处打印
  25. //问两处打印的值是否相同,为什么?
  26. }




我们先告一段落吧,要是没搞清楚,可以再看一遍上面的代码。







        通过前面的例子,相信你已经对Lambda表达式有了一个初步了解了,前一篇主要是属性Lambda的语法,这篇我们继续。

        Java8做了很多改进,以便我们少写点代码。接下来我们就来稍微了解一下,JDK自带的库文件中的一些简便工具吧。在本系列第〇篇,我就以一段“滑稽”的代码开头。说,在集合里面经常会做这种“滑稽”的处理。幸运的是,Java8给集合类提供了新技能,解决这个问题,新技能是Streams。我们现在就来了解他。

        假设我们现在要统计一个List<Person>里面的男性个数,那么代码我们通常是这样写的。


[java]​  ​​view plain​​​  ​​​copy​

  1. public int countMan(){
  2. int count = 0;
  3. Iterator<Person> iterator = personList.iterator();
  4. while(iterator.hasNext()){
  5. Person person = iterator.next();
  6. if(person.getGender().equal(“男”)){
  7. Count++;
  8. }
  9. }
  10. return count;
  11. }



他的处理过程如下图所示


Stream Java8的集合类利器——走进Java Lambda(二)_java


那么在Java8中,我们可以把上图的处理过程变成,下图这样子



Stream Java8的集合类利器——走进Java Lambda(二)_java_02


那么代码怎么写呢,可以写成这样:

Stream.filter()和Stream.count()

[java]​  ​​view plain​​​  ​​​copy​

  1. public int countMan(){
  2. return personList.stream().filter(person -> person.getGender().equal(“男”)).count();
  3. }


Stream在这里主要做了两件事情。

        第一、把所有人获取到

        第二、把“男性”过滤出来。

        我们最后调用count()就是获取结果,相当于获取新的List的size。只是相当于,仅仅为了利于我们理解方便,实际上,Stream并没有new一个新List。我们要做的事情就是,获取到Stream,告诉Stream我们要做什么,然后问Stream要结果。那要是我们不问Stream要结果,那会怎么样,答案是Stream绝对什么都不做。下面这段代码等于没有写:

[java]​  ​​view plain​​​  ​​​copy​

  1. public void function(){
  2. personList.stream().filter(person -> person.getGender().equal(“男”));
  3. }


你若不信,可以用下面一段代码​​测试​​。

[java]​  ​​view plain​​​  ​​​copy​

  1. public void function(){
  2. personList.stream().filter(person -> {
  3. System.out.println(“Go!”);
  4. person.getGender().equal(“男”);
  5. });
  6. }


        你会发现,其实他什么都没有打印,只需在后面向Stream要结果(比如调用.count()方法),那么你一定会看到输出了“​​Go​​!”。是不是很像延迟加载?Bingo!他更像是设计模式里面的“生成器模式”不是吗。等等,Stream支持延迟加载可以支持“预先加载”(Eager)吗?不能我不会BB,count()方法就是。比如现在我们要获取personList里面年龄大于7的人这样一个子集,怎么弄呢?调用collect(toList)方法。如下:

[java]​  ​​view plain​​​  ​​​copy​

  1. public void function(){
  2. List<Person> personSublist = personList.stream().filter(person -> person.getAge() > 7).collect(Collectors.toList());
  3. System.out.println(personSublist);
  4. }


这样personSublist绝逼不是null。前面刚说[不问Stream要结果,Stream绝对不会听你的要求]。这里我们向Stream要结果了吗?要了,Collectors.toList()就相当于说“嘿,把结果个我打包到新List里面”。哦,敢情这个“预先加载”是幻觉?也不能这么说,我们得看看调用的方法返回的是什么值。若Stream的方法返回的是Stream类型,那么就是延迟加载,否则不是。

Stream.map()

讲了filter我们再来看看会用得很多的map,(你若知道​JavaScript​里面Array的filter和map,可以比较一下,不知道也没关系)。我们先假设有如下这么一个List。

[java]​  ​​view plain​​​  ​​​copy​

  1. List<String> collected = new ArrayList<>();
  2. collected.add("alpha");
  3. collected.add("beta");
  4. collected.add("cool");
  5. collected.add("delta");


我们需要把里面的字母全部转换成大写,在Java8里,我们可以利用Steam这样写代码。

[java]​  ​​view plain​​​  ​​​copy​

  1. public void function(){
  2. List<String> collected = new ArrayList<>();
  3. collected.add("alpha");
  4. collected.add("beta");
  5. collected.add("cool");
  6. collected.add("delta");
  7. collected = collected.stream().map(string -> string.toUpperCase())
  8. .collect(Collectors.toList());
  9. }


map的意思是,1对1转换。也就是说map方法执行后,得到的“新集合”跟原集合的size是一样大的,而filter方法执行后得到的“子集合”可以比原集合的size小。

Map和filter的区别可以用下面图示来表示



Stream Java8的集合类利器——走进Java Lambda(二)_java_03    

Stream Java8的集合类利器——走进Java Lambda(二)_加载_04


我们用map来干什么,比如前面说过,对List<Integer>进行处理的时,会频繁地包装和解包。这样消耗内存占用时钟。我们可以用map转换成List<int>类型再计算。关于这方面的,放在后面篇章里面说。

Stream.flatMap()

除了这两个常用的需求外还有一个用于合并用的方法叫做flatMap,我们看看下面代码,使用flatMap的一个例子。


[java]​  ​​view plain​​​  ​​​copy​

  1. /**
  2. * 测试flatMap类似于addAll的功能
  3. */
  4. @Test
  5. public void flatMapTest() {
  6. List<Integer> collected0 = new ArrayList<>();
  7. collected0.add(1);
  8. collected0.add(3);
  9. collected0.add(5);
  10. List<Integer> collected1 = new ArrayList<>();
  11. collected1.add(2);
  12. collected1.add(4);
  13. collected1 = Stream.of(collected0, collected1)
  14. .flatMap(num -> num.stream()).collect(Collectors.toList());
  15. System.out.println(collected1);// 1,3,5,2,4
  16. }



或许有人会问List里面有有addAll,为什么还需要flatMap这个玩意呢。答案是这样的,flatMap可以一下子合并多个集合,并且,不会正真意义上new出新的集合出来,那么明显会占用更少的内存空间,若你要的就是合并后的新集合,那另当别论。这都是小儿科,若你想把List<Person>里面的Person里面的name和age单独放入一个集合内,这时选择flatMap来完成,也是极好的。

Stream.reduce()

介绍了过滤,转换,合并之后,再看一个,遍历操作临近元素的例子,比如现在有个放有整数的List,我们要得到其总和,那么我们可以用下面的代码来实现:

[java]​  ​​view plain​​​  ​​​copy​

  1. /**
  2. * 测试reduce方法
  3. */
  4. @Test
  5. public void reduceTest() {
  6. int sumAll = Stream.of(1, 2, 3, 4).reduce(0,
  7. (sum, element) -> sum + element);// 给一个0是用来启动,的,若给-1,结果会是9
  8. System.out.println(sumAll);// 10
  9. }


上面代码运行的逻辑如下图所示


Stream Java8的集合类利器——走进Java Lambda(二)_延迟加载_05


讲到这里,就顺便提及一下​​Java​​双冒号运算,对于C++的同学来说,这种运算符并不陌生,但是Java到目前才引用进来。对于上面例子的代码我们还可以写成这样。


[java]​  ​​view plain​​​  ​​​copy​

  1. @Test
  2. public void reduceTest() {
  3. int sumAll = Stream.of(1, 2, 3, 4).reduce(0,
  4. Integer::sum);//  Integer在Java8中提供了sum求和静态方法。


[java]​  ​​view plain​​​  ​​​copy​

  1. System.out.println(sumAll);// 10



这里只是提及,先不做细讲。

Stream.mapToInt()

在Java8里面我们可以,借助Stream对集合类进行统计。比如,我们要对一群人的年龄信息进行简单统计,那么我们可以这样写


[java]​  ​​view plain​​​  ​​​copy​

  1. @Test
  2. public void mapToIntTest(){
  3. List<Person> persons =  new PersonFactory().getPersons(8);//随机获取8个person实例
  4. IntSummaryStatistics intSummaryStatistics = persons.stream().mapToInt(person-> person.getAge()).summaryStatistics();
  5. System.out.println("最大年龄:"+intSummaryStatistics.getMax()); //最大值
  6. System.out.println("最小年龄:"+intSummaryStatistics.getMin()); //最小值
  7. System.out.println("年龄总和:"+intSummaryStatistics.getSum()); //总计
  8. System.out.println("人数:"+intSummaryStatistics.getCount());   //个数
  9. System.out.println("平均年龄:"+intSummaryStatistics.getAverage());//平均数返回的是double类型
  10. }


注意,这里我们使用了mapToT方法,T表示基础数据类型,包括int long double,注意没有float哦。mapToT方法返回值是TStream类型,TStream类包含了一些处理基础数据的方法,可以让我们更方便。我们使用mapToT的原因,不仅仅是方便,还在于性能。我们知道,因为泛型的原因,可以有List<Integer>但是不能有List<int>,这里的IntStream就相当于是List<int>,int 所占内存比Integer小。根据上面两个例子,相信你能够自己推测出​flatMapToInt()​这种flatMapToT()的用法和功能了吧,这里的T同样表示int long double基础数据类型。若你还是对MapToT方法感到困惑,不知道怎么来的,那么,接下来我们就单独讲讲上面代码里Stream.mapToInt(person -> person.getAge())这一段,这里大有乾坤哦。Stream对mapToInt 的定义是这样的:


[java]​  ​​view plain​​​  ​​​copy​

  1. IntStream mapToInt(ToIntFunction<? super T> mapper);

参数是

ToIntFunction​,这是个神马?ToIntFunction是一个函数接口,它只有一个方法。他的源码是这样写的:




[java]​  ​​view plain​​​  ​​​copy​

  1. @FunctionalInterface
  2. public interface ToIntFunction<T> {
  3. /**
  4. * Applies this function to the given argument.
  5. *
  6. * @param value the function argument
  7. * @return the function result
  8. */
  9. int applyAsInt(T value);
  10. }

意思是说,这个接口可以调用applyAsInt方法,把任意一个对象T转换成int基础数据类型。那么这段代码:




[java]​  ​​view plain​​​  ​​​copy​

  1. Stream.mapToInt(person -> person.getAge())

就是一个省略了ToIntFunction的匿名函数接口,也就相当于是:




[java]​  ​​view plain​​​  ​​​copy​

  1. ToIntFunction<Person> trans = person-> person.getAge();
  2. Stream.mapToInt(trans)

同时也相当于是代码:

(纯粹是为了复习)

[java]​  ​​view plain​​​  ​​​copy​

  1. ToIntFunction<Person> trans = new ToIntFunction<Person>() {
  2. @Override
  3. public int applyAsInt(Person person) {
  4. return person.getAge();
  5. }
  6. };
  7. Stream.mapToInt(trans);

除了

ToIntFunction​还有

ToLongFunction 、ToDoubleFunction​,它们的目的是把任意对象转换成基础数据类型。能不能把基本类型反转换成其他对象,能!但是得用别的函数接口,如

IntFunction、 LongFunction、 DoubleFunction​。更多功能请参考Java的API。




现在我们已经讲过几种Stream里面的方法了,Stream还有很多方法,这里就不一一介绍了,下一篇再说。末了要说的是,Stream可以链式调用,可以把链条拉得很长。比如,我们要获取,年龄在加上5岁后仍然小于8岁的男性,的姓氏。我们可以用类似下面的代码来实现


[java]​  ​​view plain​​​  ​​​copy​

  1. @Test
  2. public void longChainTest() {
  3. List<Person> personList = new PersonFactory().getPersons(8);//随机获取8个person实例
  4. List<String> names = new ArrayList<>(); personList.stream().map(person -> { person.setAge(person.getAge() + 5);//年龄加5岁
  5. return person; })
  6. .filter(person -> person.getAge() < 8)//年龄小于8
  7. .filter(person -> person.getGen().equals("男"))//男性
  8. .forEach(person -> names.add(person.getFirstName()));//获取姓氏
  9. System.out.println(names);}





我们先告一段落吧,要是没搞清楚,可以再看一遍上面的代码。

若你已经清楚了,那么小二我就上问题了。

1.Stream类是不是函数接口,为什么

2.以下是Stream的方法大纲的一部分(分号后表示返回类型),指出这些方法是延迟加载,还是超前加载(eager也有预先加载,贪婪加载的叫法)

sorted():Stream<T>

toArray():Object[]

skip(long):Stream<T>

(载加迟延是不toArray有只)

3.如何利用Stream来实现,将personList的姓氏和性别组合成新的List?

参考答案:

[java]​  ​​view plain​​​  ​​​copy​

  1. @Test
  2. public void ansTest() {
  3. List<Person> personList = new PersonFactory().getPersons(5);<span style="font-family: Arial, Helvetica, sans-serif;">//随机生成5个person实例
  4. List<String> names = personList
  5. .stream()
  6. .flatMap( person -> Stream.of(person.getFirstName(),
  7. person.getGen())).collect(Collectors.toList());
  8. System.out.println(names);
  9. }


4.运行和检查下面两段代码,并思考里作为无副作用的编程,我们应该提倡哪种方式?

[java]​  ​​view plain​​​  ​​​copy​

  1. @Test
  2. public void convertTest() {
  3. List<String> collected = new ArrayList<>();
  4. collected.add("alpha");
  5. collected.add("beta");
  6. collected.add("cool");
  7. collected.add("delta");
  8. collected.stream().map(string -> string.toUpperCase())
  9. .count();
  10. System.out.println(collected);//此处打印出来的是大写还是小写,为什么?
  11. }
  12. @Test
  13. public void longChainTest() {
  14. List<Person> personList = new PersonFactory().getPersons(5);//随机生成5个person实例
  15. List<Integer> ages = new ArrayList<>();
  16. personList.stream().forEach(person -> ages.add(person.getAge()));
  17. System.out.println(ages);//第一处打印
  18. ages.clear();
  19. personList.stream().map(person -> {
  20. person.setAge(person.getAge() + 10);
  21. return person;
  22. }).count();
  23. personList.stream().forEach(person -> ages.add(person.getAge()));
  24. System.out.println(ages);//第二处打印
  25. //问两处打印的值是否相同,为什么?
  26. }




我们先告一段落吧,要是没搞清楚,可以再看一遍上面的代码。