java返回map方法 java stream返回map_比较器


Java 8开始支持Stream流,Stream不同于IO流,它是对数据集合的一种高级抽象,配合Lambda通过函数式编程可以高效方便地对集合元素进行操作。这里通过具体的实例来讲解如何使用Java Stream

简介

一般地,可通过集合、数组来创建Stream数据流,数据元素在Stream的管道中单向流动。数据在流动的过程中通常会进行若干个诸如过滤、排序、映射之类的 Intermediate(中间)操作,并最终通过一个 Terminal(最终)操作来结束关闭这个数据流。通过Stream流对集合进行操作,较之传统的通过迭代器来遍历的方式可以大大地简化代码,十分优雅简洁

创建流

创建流的方式很简单,可通过如下几种方式来获取数组、集合的数据流。特别地,对于集合来说,其还可创建并行流,来提供流的操作速度


// 通过 Arrays 的静态方法 stream 来获取该数组的流


这里我们来演示下串行流Stream和并行流parallelStream的区别


public


测试结果如下所示,可以看到串行流是在一个线程中进行操作的,而并行流则是通过多个线程并行处理


java返回map方法 java stream返回map_数组_02


Intermediate(中间)操作

获取到数据流后,最重要的就是根据实际需要来对数据流中的数据进行一些处理。为此Java 8提供很多Intermediate(中间)方法以供我们使用,这里将对日常开发中高频使用的一些方法的使用进行介绍。值得一提的是,由于Intermediate方法的返回结果亦是一个Stream流,故在一个数据流中可通过链式调用的方式来调用若干个Intermediate方法

1. filter

该方法用于过滤掉我们不需要元素,当其中的lambda返回false则过滤掉该元素。这里我们过滤掉年龄不大于18的数据元素


public


测试结果如下所示:


java返回map方法 java stream返回map_java返回map方法_03


2. map

该方法可将流中的元素X映射为Y,实例如下:


public


测试结果如下所示:


java返回map方法 java stream返回map_比较器_04


3. flatMap

如果数据流中的元素是集合、数组等类型,可通过该方法将数据流中的集合、数组等类型数据分别转换为一个个的数据流,然后再将这些流扁平化为一个流。实例如下所示:


public


测试结果如下所示:


java返回map方法 java stream返回map_数据_05


4. sorted

该方法签名如下,其通过接收一个比较器来实现对流中的元素进行升序排序


Stream


相信大家对于利用lambda来创建一个比较器的方式已经很熟悉了,这里着重介绍下如何Comparator接口的静态方法comparing来创建比较器。其JDK实现如下,可以看到其创建一个指定排序键的比较器。由于该比较器默认是升序的,故若需要降序的比较器,可通过Comparator接口的默认方法reversed来实现


public


实例代码如下:


public


测试结果如下所示:


java返回map方法 java stream返回map_比较器_06


5. limit

该方法用于取数据流中的前n个元素,实例如下所示:


public


测试结果如下所示:


java返回map方法 java stream返回map_java8 list转map_07


6. skip

该方法用于跳过数据流中的前n个元素,实例如下所示:


public


测试结果如下所示:


java返回map方法 java stream返回map_数组_08


7. distinct

该方法用于对数据流中的重复元素进行去重,实例如下所示:


public


测试结果如下所示:


java返回map方法 java stream返回map_java返回map方法_09


8. peek

现在大家可能已经看到Stream大大方便了对集合元素的操作处理,但是你会发现一个新的问题没办法观察流中的数据的流动情况,而窥视方法peek则可以通过打印当前流元素的信息来帮助我们了解数据的流动情况。示例代码如下所示:


public


测试结果如下所示,通过输出信息,我们还可以发现Stream对于元素的处理是将元素一个一个的垂直流动,而非我们以为的将全部元素先执行操作一再执行操作二这样的水平流动


java返回map方法 java stream返回map_数组_10


Terminal(最终)操作

当数据流经过若干个Intermediate中间操作后,还需要通过一个Terminal(最终)操作来关闭这个流。故在一个Stream,有且只有一个Terminal操作

1. forEach

该方法遍历流中的元素,并同时关闭这个流


public


测试结果如下:


java返回map方法 java stream返回map_数据_11


2. toArray

该方法可以将流中的元素转为数组,示例如下:


public


测试结果如下:


java返回map方法 java stream返回map_java8 list转map_12


3. collect

该方法将接收一个收集器来收集流中的元素数据,毕竟我们将集合转为流处理之后通常还是期望将处理后的元素再转回为集合,以供我们后续使用。故Java直接在Collectors类内置了很多收集器,例如上文代码中大量出现的Collectors.toSet()、Collectors.toList(),相信大家也能猜出来这些收集器的作用了,其就是把流里的数据转到Set、List集合中去。类似地,Collectors.toMap() 收集器(Function.identity() 可用于表示元素本身 )可用于将流转为Map,Collectors.groupingBy()可将流中元素按指定键进行分组,并将该键作为所生成Map的key。示例如下:


public


测试结果如下所示:


java返回map方法 java stream返回map_数组_13


这里再将介绍一种通过 Collectors.joining() 收集器来优雅地实现字符串拼接,该收集器可以指定拼接的分隔符及拼接后的前后缀,示例如下所示:


public


测试结果如下所示:


java返回map方法 java stream返回map_比较器_14


4. reduce

该方法可以从Stream流中计算生成一个值,其常用的两种重载形式如下,accumulator参数是一个函数式接口,identity参数则为计算初值。乍一看会觉得reduce方法很复杂,不知如何使用。下面我们来结合具体示例进行说明就会发现虽然看起来复杂但实际用起来还是比较方便简单的


Optional


accumulator参数接收一个含有两个参数的lambda表达式 (param1, param2) ->expression,其中param1参数为上一次expression计算结果,param2参数是从Stream流遍历时中获取的元素,expression表达式计算的结果将会传递并用于下一次计算的param1当中去。对于reduce(BinaryOperator accumulator)方法而言,第一次计算时,param1为Stream流中的第一个元素,param2为Stream流中的第二个元素,示例代码如下所示:


public


测试结果如下所示,从demo2中的运行结果,我们也可以看出, 第一次计算(红框)时lambda参数分别取了流中的第一、二个元素,之后计算(黄框)时lambda参数是上次计算结果和从流中遍历获取的元素


java返回map方法 java stream返回map_数据_15


另外一种重载形式reduce(T identity, BinaryOperator accumulator),其可通过identity参数来指定accumulator中lambda第一次计算时param1参数的初值,而不是用流中的第一个元素作为初值。示例代码如下所示:


public


测试结果如下所示:


java返回map方法 java stream返回map_数据_16


特性

  • Laziness 惰性

如上文所言,在Stream中Intermediate 操作允许有零个或若干个,但 Terminal 操作有且只有一个。在一个流中不可有多个Terminal操作,相信大家已经知道了,因为 Terminal 操作会关闭结束流,所以如果存在多个的话,会导致后面的 Terminal 方法出现无流操作的情况。而之所以要求流中必须要有 Terminal 操作,实际上是因为Intermediate操作的Laziness特性所导致的。换句话说,流中的Intermediate操作实际上不是立刻执行生效,只有当Terminal 操作存在后,这个流才开始遍历工作

  • 不影响原始数据

Stream是从原始数据(数组、集合等)创建的一个新流,我们对流的这个的各种操作都不会影响原始的数组、集合数据

参考文献

  1. Java核心技术·卷II 凯.S.霍斯特曼著