吃透JAVA的Stream流操作,多年实践总结

在JAVA中,触及到对数组Collection等调集类中的元素进行操作的时分,通常会经过循环的办法进行逐一处理,或许运用Stream的办法进行处理。

例如,现在有这么一个需求:

从给定语句中回来单词长度大于5的单词列表,按长度倒序输出,最多回来3个

JAVA7及之前的代码中,咱们会能够照如下的办法进行完成:

public List<String> sortGetTop3LongWords(@NotNull String sentence) {
    // 先切割语句,获取详细的单词信息
    String[] words = sentence.split(" ");
    List<String> wordList = new ArrayList<>();
    // 循环判别单词的长度,先过滤出契合长度要求的单词
    for (String word : words) {
        if (word.length() > 5) {
            wordList.add(word);
        }
    }
    // 对契合条件的列表依照长度进行排序
    wordList.sort((o1, o2) -> o2.length() - o1.length());
    // 判别list成果长度,假如大于3则截取前三个数据的子list回来
    if (wordList.size() > 3) {
        wordList = wordList.subList(0, 3);
    }
    return wordList;
}

JAVA8及之后的版别中,借助Stream流,咱们能够愈加优雅的写出如下代码:


public List<String> sortGetTop3LongWordsByStream(@NotNull String sentence) {
    return Arrays.stream(sentence.split(" "))
            .filter(word -> word.length() > 5)
            .sorted((o1, o2) -> o2.length() - o1.length())
            .limit(3)
            .collect(Collectors.toList());
}

直观感受上,Stream的完成办法代码愈加简练、一气呵成。很多的同学在代码中也常常运用Stream流,可是对Stream流的认知往往也是仅限于会一些简略的filtermapcollect等操作,但JAVA的Stream能够适用的场景与才能远不止这些。

吃透JAVA的Stream流操作,多年实践总结

那么问题来了:Stream相较于传统的foreach的办法处理stream,到底有啥优势

这儿效率的拼音咱们能够字符间距怎么加宽先放置这个问题,先整体全面的了解下Stream,然后再来讨论下这个问题。

笔者结合在团队中多年的代码检视遇到的状况,结合平时项目编码实践经验,对Stream的核心要点与易混杂用法典型运用场景等进行了详细的梳理总结,希望能够协助大家对Stream有个更全面的认知,也能够愈加高效的应用到项目开发中去。javascript

Stream初相识

归纳讲,能够将Stream流操作分为3种类型

  • 创立Stream
  • Stream中心处理
  • 停止Steam

吃透JAVA的Stream流操作,多年实践总结

每个appleStream管道操作类型都包含若干API办法,先列举下各个API办法的功用介绍。

  • 开端管道java是什么意思

首要担任新效率公式建一个Stream流,或许根据现有的数组、List、Set、Map等调集类型目标创立出新的Stream流。

API功用阐明
stream()创立出一个新的stream串行流目标
parallelStream()创立出一个可并行履行的stream流目标
Stream.of()经过给定的一系列元素创立一个新的Str效率英文翻译eam串行流目标

吃透JAVA的Stream流操作,多年实践总结

  • 中心管道

担任对Stream进行处理操作,并回来一个新的Stream目标,中心效率符号管道操作能够进行叠加

API功用阐明
filter()依照条件过滤契合要求的元素, 回来新的applestream流
map()将已有元素转化为另一个目标类型,1对1逻辑,回approach来新的stream流
flatMap()将已有元素转化为另一个目标类型,效率计算公式一对多逻辑,即本来一个元素目标可能会转化为1个或许多个新类型的元素,回来新的stream流
lim字符间距在哪里设置it()仅保存调集前面指定个数的元素,回来新的stream流
skip()越过调集approve前面指定程序员需要什么学历个数的元素,回来新的stre字符串逆序输出am流
concat()将两个流的数据兼并起来为1个新的流,回来新的stream流
dist程序员那么可爱电视剧免费观看inct()对Stream中一切元素进行去重,回来新的str程序员需要什么学历eam流
sorted()对stream效率符号中一切的元素依照指定规矩进行排序,回来新的stream流
peek()对stream流中的每个元素进行逐一遍历处理,回来处理后的stream流

吃透JAVA的Stream流操作,多年实践总结

  • 停止管道

望文生义,经过停止管道操作之后,Stream流将会结束,终究可能会履行某些逻辑处理,或许是依照要求回来某些履行后的成果数据。

API功用阐明
count()回来stream处理后终究的元素个数
max()回来stream处理后的元素最大值
min()回来stream处理后的效率意识方面存在的问题元素最小值
findFi效率rst()找到第一个契合条件的元素时则停止流处理
findAny()找到任何一个契合条件的元素时则退出流处理,这个关于串行流时与findFirst相同,关于并行流时比较高效,任何分片中找到都会停止后续核approach算逻辑
anyMatch()回来一个boolean值,类似于isContains(),用于判别是否有契合条件的元素
allMatch()回来一个字符串是什么意思boolean值,用于判别是否一切元素都契合条件
noneMatch()回来一个boolean值, 用于判别是否一切元素都不契合条件
collect()将流通换为指定的类型,经过Collectors进行指定
toArray()将流通换为数组
itera程序员怎么学tor(approve)将流通换为Iterator目标
foreach()无回来值,对元素进行逐一遍历字符间距加宽2磅,然后履行给定的处理逻辑java怎么读

Stream办法运用

map与flatMap

mapflatMap都是用于转化已有的元素为其它元素,区别点在于:

  • map 有必要是1对1的,即每个元素都只能转化字符间距怎么加宽为1个新的元素
  • flatMap 能够是一对多的,即每个元素都能够转化为1个或许多个新的元素

吃透JAVA的Stream流操作,多年实践总结

比方:有一个字符串ID列表,现在需字符求将其转为User目标效率列表。能够运用map来完成:


/**
 * 演示map的用途:1对1转化
 */
public void stringToIntMap() {
    List<String> ids = Arrays.asList("205", "105", "308", "469", "627", "193", "111");
    // 运用流操作
    List<User> results = ids.stream()
            .map(id -> {
                User user = new User();
                user.setId(id);
                return user;
            })
            .collect(Collectors.toList());
    System.out.println(results);
}

履行之后,会发现每一个元素都被转化为对应新的元素,可是前后总元字符串是什么意思素个数是一致的:


[User{id='205'}, 
 User{id='105'},
 User{id='308'}, 
 User{id='469'}, 
 User{id='627'}, 
 User{id='193'}, 
 User{id='111'}]

吃透JAVA的Stream流操作,多年实践总结

再比方:现有一个语句列表,需求将语句中每个单词都提取出来得到一个一切单词列表。这种状况用map就搞不定了,需求flatMap效率意识方面存在的问题上场了:


public void stringToIntFlatmap() {
    List<String> sentences = Arrays.asList("hello world","Jia Gou Wu Dao");
    // 运用流操作
    List<String> results = sentences.stream()
            .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
            .collect(Collectors.toList());
    System.out.println(results);
}

履行成果如下,能够看到成果列表中元素个数是比原始列表元素个数要多的:


[hello, world, Jia, Gou, Wu, Dao]

这儿需求弥补一句,flatMap操作的时分其实是先每个元素处理并回来一个新的Stream,然后将多个Stream打开兼并为了appstore一个完整的新的Stream,如下:

吃透JAVA的Stream流操作,多年实践总结

peek和foreach办java编译器

peekforeach,都能够字符串逆序输出用于java是什么意思对元素进行遍历然后逐一的进行处理。

但根据前面的介绍,peek属于中心办程序员工作一年后工资,而foreach属于停止办法。这也字符型变量就意味着peek只能作为管道中途的一个处理步骤,而没法直接履行得到成果,其后边有必要还要有其它停止操作的时分才会被履行;而foreach作为无回程序员工资一般多少来值的停止办法,则能够直效率的英文接履行相关操作。


public void testPeekAndforeach() {
    List<String> sentences = Arrays.asList("hello world","Jia Gou Wu Dao");
    // 演示点1: 仅peek操作,终究不会履行
    System.out.println("----before peek----");
    sentences.stream().peek(sentence -> System.out.println(sentence));
    System.out.println("----after peek----");
    // 演示点2: 仅foreach操作,终究会履行
    System.out.println("----before foreach----");
    sentences.stream().forEach(sentence -> System.out.println(sentence));
    System.out.println("----after foreach----");
    // 演示点3: peek操作后边添加停止操作,peek会履行
    System.out.println("----before peek and count----");
    sentences.stream().peek(sentence -> System.out.println(sentence)).count();
    System.out.println("----after peek and count----");
}

输出成果能够看出,peek独自调用时并没有被履行、但peek后边加上停止操作之后便能够被履行,javaeef程序员需要什么学历oreach能够直接被履行:


----before peek----
----after peek----
----before foreach----
hello world
Jia Gou Wu Dao
----after foreach----
----before peek and count----
hello world
Jia Gou Wu Dao
----after peek and count----

吃透JAVA的Stream流操作,多年实践总结

filter、sorted、distinct、limit

这几个都是常程序员用的Stream的中心操作办法,详细的办法的含义在上面的表格里边效率的拼音有阐明。详细运用的时分,能够根据需求选择一个或许多个进行组合运用,或许同时运用多个相同办法的组合


public void testGetTargetUsers() {
    List<String> ids = Arrays.asList("205","10","308","49","627","193","111", "193");
    // 运用流操作
    List<Dept> results = ids.stream()
            .filter(s -> s.length() > 2)
            .distinct()
            .map(Integer::valueOf)
            .sorted(Comparator.comparingInt(o -> o))
            .limit(3)
            .map(id -> new Dept(id))
            .collect(Collectors.toList());
    System.out.println(results);
}

上面的代码片段的处理逻辑很清晰:

  1. 运用filter过滤掉不契合条件的数据
  2. 经过distinct对存量元素进行去重操作
  3. 经过map操作将字符串转成整数类型
  4. 借助sorted指定依照数字巨细正序排列
  5. 运用limit截取排在前3位的元素
  6. 又一次运用map将id转为Dept目标类型
  7. 运用collect停止字符常量操作将终究处理后的数据搜集到list程序员是做什么的

输出成果:

[Dept{id=111},  Dept{id=193},  Dept{id=205}]

吃透JAVA的Stream流操作,多年实践总结

简略成果停止办法

依照前面介绍的,停止办法里边像countmaxminfindAnyfindFirstanyMatc效率符号hallMatchnonneMatch等办法,均属于这儿说的简略成果停止办法。所谓简略,指的是其成果方法是数字、布尔值或许Optional目标值等。


public void testSimpleStopOptions() {
    List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
    // 核算stream操作后剩下的元素个数
    System.out.println(ids.stream().filter(s -> s.length() > 2).count());
    // 判别是否有元素值等于205
    System.out.println(ids.stream().filter(s -> s.length() > 2).anyMatch("205"::equals));
    // findFirst操作
    ids.stream().filter(s -> s.length() > 2)
            .findFirst()
            .ifPresent(s -> System.out.println("findFirst:" + s));
}

履行后成果为:


6
true
findFirst:205

吃透JAVA的Stream流操作,多年实践总结

避坑提示

这儿需求弥补提示下,一旦一个Stream被履行了停止操作之后,后续便不能够再读这个流履行其他的操作了,不然会报错,看下效率计算公式面示例:


public void testHandleStreamAfterClosed() {
    List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
    Stream<String> stream = ids.stream().filter(s -> s.length() > 2);
    // 核算stream操作后剩下的元素个数
    System.out.println(stream.count());
    System.out.println("-----下面会报错-----");
    // 判别是否有元素值等于205
    try {
        System.out.println(stream.anyMatch("205"::equals));
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("-----上面会报错-----");
}

履行的时分,成果如下:


6
-----下面会报错-----
java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
	at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:449)
	at com.veezean.skills.stream.StreamService.testHandleStreamAfterClosed(StreamService.java:153)
	at com.veezean.skills.stream.StreamService.main(StreamService.java:176)
-----上面会报错-----

因为stream现已被履行count()停止办法了,所以对stream再履行anyMatchjava编译器办法的时分,就会报错stream has already been operated u效率pon or closed,这一点在运用的时分需求特别注意。

吃透JAVA的Stream流操作,多年实践总结

成果搜集停止办法

因为Stream首要用于对调集数据的处理场景,所以除了上面几种获取简略成果的停止办法之外,更多的场景是获取一个调集类的成果目标,Java比方List、Set或许HashMap等。

这儿就需求collect办法出场了,它能够支撑生成如下类型的成果数据:

  • 一个调集类,比方List、Set或许HashMap等
  • Stri程序员计算器ngBuilder目标,支撑将多个字符串进行拼接处理并输出Java拼接后成果
  • 一个能够记载个数或许核算总和的目标(数据批量运算核算

吃透JAVA的Stream流操作,多年实践总结

生成调集

应该算是collect最常被运用到的一个场景了:


public void testCollectStopOptions() {
    List<Dept> ids = Arrays.asList(new Dept(17), new Dept(22), new Dept(23));
    // collect成list
    List<Dept> collectList = ids.stream().filter(dept -> dept.getId() > 20)
            .collect(Collectors.toList());
    System.out.println("collectList:" + collectList);
    // collect成Set
    Set<Dept> collectSet = ids.stream().filter(dept -> dept.getId() > 20)
            .collect(Collectors.toSet());
    System.out.println("collectSet:" + collectSet);
    // collect成HashMap,key为id,value为Dept目标
    Map<Integer, Dept> collectMap = ids.stream().filter(dept -> dept.getId() > 20)
            .collect(Collectors.toMap(Dept::getId, dept -> dept));
    System.out.println("collectMap:" + collectMap);
}

成果如下:


collectList:[Dept{id=22}, Dept{id=23}]
collectSet:[Dept{id=23}, Dept{id=22}]
collectMap:{22=Dept{id=22}, 23=Dept{id=23}}

吃透JAVA的Stream流操作,多年实践总结

生成拼接字符串

将一个List或许数组中的值拼接到一个字符串里并以逗程序员工资一般多少号分隔开,这个场景相信大家都不陌生吧?

假如经过forjava培训环和Strin字符gBuilder去循环拼接,还得考虑下终究一个逗号如何处理的问题,很繁琐:


public void testForJoinStrings() {
    List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
    StringBuilder builder = new StringBuilder();
    for (String id : ids) {
        builder.append(id).append(',');
    }
    // 去掉末尾多拼接的逗号
    builder.deleteCharAt(builder.length() - 1);
    System.out.println("拼接后:" + builder.toString());
}

可是现在有了Stream,运用collect能够轻而易举的完成:


public void testCollectJoinStrings() {
    List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
    String joinResult = ids.stream().collect(Collectors.joining(","));
    System.out.println("拼接后:" + joinResult);
}

两种办法都能java面试题够得到完全相同的成果,字符串逆序输出但Stream的办法更优雅:

拼接后:205,10,308,49,627,193,111,193

吃透JAVA的Stream流操作,多年实践总结

数据批量数学运算

还有一种字符间距怎么加宽场景,实际运用的时分程序员那么可爱可能会比较少,就是运用collect生成数字数据的总和信息,也能够了解下完成办法:


public void testNumberCalculate() {
    List<Integer> ids = Arrays.asList(10, 20, 30, 40, 50);
    // 核算平均值
    Double average = ids.stream().collect(Collectors.averagingInt(value -> value));
    System.out.println("平均值:" + average);
    // 数据核算信息
    IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value));
    System.out.println("数据核算信息: " + summary);
}

上面的例子中,运用collect办法来对字符lisjava编译器t中元素值进行数学运算,成果如下:


平均值:30.0
总和: IntSummaryStatistics{count=5, sum=150, min=10, average=30.000000, max=50}

吃透JAVA的Stream流操作,多年实践总结

并行Stream

机制阐明

运用并行流,能够有效运用核算机的多CP字符串逆序输出U硬件,application提升逻辑的履行速度。并行流经过将一整个stream划分为多个片段,然后对各个分片流并行履行处理逻辑,终究将各个分片流的履行成果汇总为一个整体流。

吃透JAVA的Stream流操作,多年实践总结

约束与限制

并行流类似于多线程在并行处理,所以与多线程场景相关的一些问题同样会存在,比方死锁字符间距等问题,所以在并行流停字符串是什么意思止履行的函数逻辑,有必要要确保线程安全

吃透JAVA的Stream流操作,多年实践总结

答复最初的问题

到这儿,关application于JAVA Stream的相关概念与用法介绍,根本就讲完了。咱们再把焦点切回本文刚开端时提及的一个问题:

Stream相较于传统的foreach的办法处理streappointmentam,到底有啥优势

根据前面的介绍,咱们应该能够得出如下几点答案:

  • 代码更简练、偏声明式的编码风格,更容易体现出appearance代码的逻辑意图
  • 逻辑间解耦,一个stream中心处理逻辑,无需重视上游与字符间距在哪里设置下流的内容,只需求按约好完成本身逻辑即可
  • 并行流场景功率会比迭代器逐一循环更高
  • 函数式接口,效率高发票查验延迟履行的特性,中心管道操作不Java管有多少步骤都不会立即履行,只要遇到停止操作的时分才会开字符间距怎么加宽端履行,能够避免一些中心不必要的操作消耗

字符常量然了,Stream也不全是长处,在有些方面也有其弊端:

  • 代码调测debug不便
  • 程序员从前史写法切换到Stream时,程序员工资一般多少需求一定的习惯时间

吃透JAVA的Stream流操作,多年实践总结

总结

好啦,关于JAVA Stream的理解要点与运用技能的阐述就先到这儿啦。那经过上面的介绍,各位小伙伴们是否现已跃跃欲试了呢?快去项目中运用体会下吧!当然啦效率意识方面存在的问题,假如有疑问,也欢迎找我一同讨论讨论咯。

此外

  • 关于Stream程序员怎么学中collect的分组、效率符号分片等进阶操作,以及对并行流的深入讨论,因为apple字符型变量及内容比APP较多且相对独立,我会在后续的文档中打程序员需要什么学历开专appear门介绍下,假如有爱好的话,能够点个重视、避免迷路。

  • 关于本文中触及的演示代码的完整示例,我现已字符串是什么意思整理并提交到github中,假如您有需求,能够自取:github.com/veezean/Jav…

吃透JAVA的Stream流操作,多年实践总结

我是悟道,聊技能、又不只是聊技能~

假如觉得有用,请点个重视,也可重java怎么读视下我的大众号【架构悟道】,获取更及时更新。

等待与你一同讨论,一同生长为更好的自己。

吃透JAVA的Stream流操作,多年实践总结


approach正在参与技能社区创作者签约计划招javascript募活动,点击链接报名投稿。

发表评论

提供最优质的资源集合

立即查看 了解详情