博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Lambda表达式的应用--Stream API操作集合框架
阅读量:2144 次
发布时间:2019-04-30

本文共 7347 字,大约阅读时间需要 24 分钟。

概述

这一篇文章,要有Lambda表达式的基础,在此对Lambda的使用不做介绍,请看前一篇对Java lambda的使用。

学以致用,学习Lambda表达式,就冲java 8 新的库Stream来说就够本。一定将Lambda和Stream分开来学,先把Lambda表达式玩转了,在深入学习Stream就容易了,因为里面大量的lambda的写法,不要因为lambda不熟练影响了Stream API的使用,时刻提醒自己方法内传递的是函数式接口的对象,lambda只不过是重写了接口的方法体。

前提

使用Stream前,先来着重学习几个java8函数式接口。

在学习了解之前,希望大家能记住几个单词,掌握这几个单词,什么3,40个官方的函数接口都是小问题了,不信的话接着往下看啦。ok,那这几个单词呢分别是supplier提供者 ,consumer消费者 ,function函数,Predicate断言, operation运算符, binary二元(就是数学里二元一次方程那个二元,代表2个的意思)。

  • Function<T, R>:接受一个参数T,返回结果R
  • Predicate<T>:接受一个参数T,返回boolean
  • Supplier<T>:不接受任何参数,返回结果T
  • Consumer<T>:接受一个参数T,不返回结果
  • UnaryOperator<T>:继承自Function<T, T>,接受一个参数T,返回相同类型T的结果
  • BiFunction<T, U, R>:接受两个参数T和U,返回结果R
  • BinaryOperator<T>:继承自BiFunction<T, T, T>,接受两个相同类型T的参数,返回相同类型T的结果

请将上面几个函数式接口,清晰的记在脑子里(前面4个使用量大),这几个接口的方法其实是高度再高度的抽象,根据实际使用几乎涵盖了方法应该有情况。具体如下:

Functio<t,r>接口

function,顾名思义,函数的意思,这里的函数是指数学上的函数哦,你也可以说是严格函数语言中的函数,例如haskell里的,他接受一个参数,返回一个值,永远都是这样,是一个恒定的,状态不可改变的方法。其实想讲函数这个彻底将明白可以再开一篇博客了,所以这里不详细的说了。

上面说到,函数接口是对行为的抽象,因此我方便大家理解,就用java中的方法作例子。

Fcuntion接口是对接受一个T类型参数,返回R类型的结果的方法的抽象,通过调用apply方法执行内容。

public static void main(String[] args) {        Function
function= s -> s + "你好"; String a = function.apply("helloWorld!"); System.out.println(a); //控制台输出 helloWorld!你好 }

Consumer 接口

Consumer 接口翻译过来就是消费者,顾名思义,该接口对应的方法类型为接收一个参数,没有返回值,可以通俗的理解成将这个参数'消费掉了',一般来说使用Consumer接口往往伴随着一些期望状态的改变或者事件的发生,例如最典型的forEach就是使用的Consumer接口,虽然没有任何的返回值,但是却向控制台输出了语句。

Consumer 使用accept对参数执行行为

public static void main(String[] args) {        Consumer
printString = s -> System.out.println(s); printString.accept("helloWorld!"); //控制台输出 helloWorld! }

Supplier 接口

Supplier 接口翻译过来就是提供者,和上面的消费者相反,该接口对应的方法类型为不接受参数,但是提供一个返回值,通俗的理解为这种接口是无私的奉献者,不仅不要参数,还返回一个值,使用get()方法获得这个返回值

public static void main(String[] args) {      Supplier
getInstance = () -> "HelloWorld!"; System.out.println(getInstance.get()); // 控偶值台输出 HelloWorld}

Predicate 接口

predicate<t,boolean> 谓语接口,顾名思义,中文中的‘是’与‘不是’是中文语法的谓语,同样的该接口对应的方法为接收一个参数,返回一个Boolean类型值,多用于判断与过滤,当然你可以把他理解成特殊的Funcation<t,r>,但是为了便于区分语义,还是单独的划了一个接口,使用test()方法执行这段行为

public static void main(String[] args) {     Predicate
predOdd = integer -> integer % 2 == 1; System.out.println(predOdd.test(5)); //控制台输出 true }

其他的接口

介绍完正面这四种最基本的接口,剩余的接口就可以很容易的理解了,java8中定义了几十种的函数接口,但是剩下的接口都是上面这几种接口的变种,大多为限制参数类型如IntPredicate等。

熟悉了以上几个接口,咱们就可以踏实的学习集合框架的新操作了。

Java 8 Stream

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 取重,排序,聚合等。

元素流在管道中经过一系列中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

先来看看一个例子:

List
transactionsIds = widgets.stream() .filter(b -> b.getColor() == RED) .sorted((x,y) -> x.getWeight() - y.getWeight()) .mapToInt(Widget::getWeight) .sum();

什么是Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

创建Stream

1.Stream接口的静态工厂方法

  • of(T... values):返回含有多个T元素的Stream
  • generate(Supplier<T> s):返回一个无限长度的Stream

2.在 Java 8 中, 集合接口有两个方法来生成流:(使用最多就是对集合创建stream)

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。

3.数组的创建方法:

  • Arrays.stream(T[] array)

4.其他方式创建

  • Random.ints()
  • BitSet.stream()
  • Pattern.splitAsStream(java.lang.CharSequence)
  • JarFile.stream()
List
strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");List
filtered = strings.stream() .filter(string -> !string.isEmpty()) .collect(Collectors.toList());

对于Stream的使用就是使用其API,关键是其中方法的参数是各种各样的函数式接口,来看看Stream中的方法:

forEach(Collection接口中添加新forEach的方法,使用相同)

void forEach(Consumer<? super T> action);

Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

Random random = new Random();random.ints().limit(10).forEach(System.out::println);

map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong。其实很好理解,如果想将原Stream中的数据类型,转换为double,int或者是long是可以调用相对应的方法。以下代码片段使用 map 输出了元素对应的平方数:

List
numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);// 获取对应的平方数List
squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

filter

Stream<T> filter(Predicate<? super T> predicate);

filter方法对原Stream按照指定条件过滤,在新建的Stream中,只包含满足条件的元素,将不满足条件的元素过滤掉。以下代码片段使用 filter 方法过滤出空字符串:

List
strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");// 获取空字符串的数量int count = strings.stream().filter(string -> string.isEmpty()).count();

limit

Stream<T> limit(long maxSize);

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

Random random = new Random();random.ints().limit(10).forEach(System.out::println);

sorted

Stream<T> sorted(Comparator<? super T> comparator);

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:(有无参重载)

Random random = new Random();random.ints().limit(10).sorted().forEach(System.out::println);

peek

Stream<T> peek(Consumer<? super T> action);

peek方法生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数,并且消费函数优先执行

Stream.of(1, 2, 3, 4, 5)        .peek(integer -> System.out.println("accept:" + integer))        .forEach(System.out::println);

skip

Stream<T> skip(long n);

skip方法将过滤掉原Stream中的前N个元素,返回剩下的元素所组成的新Stream。如果原Stream的元素个数大于N,将返回原Stream的后(原Stream长度-N)个元素所组成的新Stream;如果原Stream的元素个数小于或等于N,将返回一个空Stream。

Stream.of(1, 2, 3,4,5)     .skip(2)     .forEach(System.out::println); // 打印结果 // 3,4,5

并行(parallel)程序

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

List
strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");// 获取空字符串的数量int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

concat

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);

concat方法将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都时排序的,则新Stream也是排序的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的;若关闭新的Stream时,原两个输入的Stream都将执行关闭处理。

Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5))       .forEach(integer -> System.out.print(integer + "  "));// 打印结果// 1  2  3  4  5

distinct

distinct方法以达到去除掉原Stream中重复的元素,生成的新Stream中没有没有重复的元素。

Stream.of(1,2,3,1,2,3)        .distinct()        .forEach(System.out::println); // 打印结果:1,2,3

count

count方法将返回Stream中元素的个数。

long count = Stream.of(1, 2, 3, 4, 5)        .count();System.out.println("count:" + count);// 打印结果:count:5

Collectors 合并器

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

List
strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");List
filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选列表: " + filtered);String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));System.out.println("合并字符串: " + mergedString);

等还有其他一些API使用不是很频繁,如需使用请查看官方API。

(完)

转载地址:http://rqhgf.baihongyu.com/

你可能感兴趣的文章
(PAT 1080) Graduate Admission (排序)
查看>>
Play on Words UVA - 10129 (欧拉路径)
查看>>
mininet+floodlight搭建sdn环境并创建简答topo
查看>>
【linux】nohup和&的作用
查看>>
Set、WeakSet、Map以及WeakMap结构基本知识点
查看>>
【NLP学习笔记】(一)Gensim基本使用方法
查看>>
【NLP学习笔记】(二)gensim使用之Topics and Transformations
查看>>
【深度学习】LSTM的架构及公式
查看>>
【python】re模块常用方法
查看>>
剑指offer 19.二叉树的镜像
查看>>
剑指offer 20.顺时针打印矩阵
查看>>
剑指offer 21.包含min函数的栈
查看>>
剑指offer 23.从上往下打印二叉树
查看>>
剑指offer 25.二叉树中和为某一值的路径
查看>>
剑指offer 60. 不用加减乘除做加法
查看>>
Leetcode C++《热题 Hot 100-13》234.回文链表
查看>>
Leetcode C++《热题 Hot 100-14》283.移动零
查看>>
Leetcode C++《热题 Hot 100-15》437.路径总和III
查看>>
Leetcode C++《热题 Hot 100-17》461.汉明距离
查看>>
Leetcode C++《热题 Hot 100-18》538.把二叉搜索树转换为累加树
查看>>