1.概述

转载并且补充:java8 stream的这些开发技巧,你值得好好收藏

2.stream的继承关系

stream的继承关系

Stream、IntStream、LongStream、DoubleStream的父接口都是BaseStream。BaseStream的四个子接口方法都差不多,只是IntStream、LongStream、DoubleStream直接存储基本类型,可以避免自动装/拆箱,效率会更高一些。但是,我们实际上使用Stream更多一些。

我们再看看stream的工作流程图:


stream的工作流程图

为什么要学stream的链式编程方式

业务需求1:指定一个字符串数组,找出里面相同的元素,并且统计重复的次数。

我们以前大概是这样做的:

public class CountTest {@Testpublic void testCount1() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");Map<String, Long> countMap = new HashMap<>();for (String data : list) {Long aLong = countMap.get(data);if (Objects.isNull(aLong)) {countMap.put(data, 1L);} else {countMap.put(data, ++aLong);}}countMap.forEach((key, value) -> System.out.println("key:" + key + " value:" + value));}
}

执行结果:

key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1

我们再看看如果用java8的stream可以怎么做:

public class CountTest {@Testpublic void testCount2() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));countMap.forEach((key, value) -> System.out.println("key:" + key + " value:" + value));}
}

执行结果:

key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1

我们可以看到testCount1和testCount2执行结果相同,仅仅一行代码:Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 就可以实现上面testCount1中多行代码的逻辑。

业务需求2:从一个指定的字符串数组中,查找指定的字符串是否存在

我们以前大概是这样做的:

public class FindTest {@Testpublic void testFind1() {String findStr = "bd";List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");boolean match = false;for (String data : list) {if (data.equals(findStr)) {match = true;break;}}//结果:match:trueSystem.out.println("match:" + match);}
}

我们再看看如果用java8的stream可以怎么做:

public class MatchTest {@Testpublic void testFind2() {String findStr = "bd";List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");boolean match = list.stream().anyMatch(x -> x.equals(findStr));//结果:match:trueSystem.out.println("match:" + match);}
}

我们可以看到调用testFind1和testFind2方法执行结果也是一样的。但是,用java8 stream的语法,又只用一行代码就完成功能了,真棒。

java8 stream超详细用法指南
stream的操作符大体上分为两种:中间操作符和终止操作符

中间操作:

1.filter(T-> boolean)

过滤数据,保留 boolean 为 true 的元素,返回一个集合

public class FilterTest {@Testpublic void testFilter() {List<Integer> list = Lists.newArrayList(20, 23, 25, 28, 30, 33, 37, 40);//从指定数据集合中过滤出大于等于30的数据集合List<Integer> collect = list.stream().filter(x -> x >= 30).collect(Collectors.toList());//结果:[33, 37, 40]System.out.println(collect);}
}

collect(Collectors.toList())可以把流转换为 List 类型,collect实际上是一个终止操作。

2.map(T -> R)

转换操作符,可以做数据转换,比如:把字符串转换成int、long、double,或者把一个实体转换成另外一个实体。包含:map,mapToInt、mapToLong、mapToDouble

public class MapTest {@Testpublic void testMap() {List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");List<Long> collect1 = list.stream().map(x -> Long.parseLong(x)).collect(Collectors.toList());//结果:[1, 2, 3, 4, 5, 6]System.out.println(collect1);//结果:111111list.stream().mapToInt(x -> x.length()).forEach(System.out::print);System.out.println("");//结果:111111list.stream().mapToLong(x -> x.length()).forEach(System.out::print);System.out.println("");//结果:1.01.01.01.01.01.0list.stream().mapToDouble(x -> x.length()).forEach(System.out::print);}
}

3.flatMap(T -> Stream)

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

public class FlatMapTest {@Testpublic void testFlatMap() {List<List<String>>  list = new ArrayList<List<String>>(){{add(Lists.newArrayList("a","b","c"));add(Lists.newArrayList("d","e","f"));add(Lists.newArrayList("j","k","y"));}};//结果:[[a, b, c], [d, e, f], [j, k, y]]System.out.println(list);List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList());//结果:[a, b, c, d, e, f, j, k, y]System.out.println(collect);}
}

我们可以看到flatMap可以轻松把字符串的二维数据变成一位数组。

4.distinct

去重,类似于msql中的distinct的作用,底层使用了equals方法做比较。

public class DistinctTest {@Testpublic void testDistinct() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");List<String> collect = list.stream().distinct().collect(Collectors.toList());//结果:[a, b, ab, abc, abcd, bd]System.out.println(collect);}
}

其实,去重还有另外一种办法,可以用Collectors.toSet(),后面会讲到。

5.sorted

对元素进行排序,前提是实现Comparable接口,当然也可以自定义比较器。

public class SortTest {@Testpublic void testSort() {List<Integer> list = Lists.newArrayList(5, 3, 7, 1, 4, 6);List<Integer> collect = list.stream().sorted((a, b) -> a.compareTo(b)).collect(Collectors.toList());//结果:[1, 3, 4, 5, 6, 7]System.out.println(collect);}
}

6.limit

限流操作,有点类似于mysql中的limit功能,比如:有10个元素,只取前面3个元素

public class LimitTest {@Testpublic void testLimit() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");List<String> collect = list.stream().limit(3).collect(Collectors.toList());//结果:[a, b, ab]System.out.println(collect);}
}

7.skip

跳过操作,比如:有个10个元素,从第5个元素开始去后面的元素

public class SkipTest {@Testpublic void testSkip() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");List<String> collect = list.stream().skip(5).collect(Collectors.toList());//结果:[ab, a, abcd, bd, abc]System.out.println(collect);}
}

8.peek

挑出操作,

public class PeekTest {@Testpublic void testPeek() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");//结果:abababcaabaabcdbdabclist.stream().peek(x -> x.toUpperCase()).forEach(System.out::print);}
}

眼尖的朋友会发现,进行x.toUpperCase()转换为大写功能,但是实际上没有生效。把peek改成map方法试试:

public class PeekTest {@Testpublic void testPeek() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");//结果:ABABABCAABAABCDBDABClist.stream().map(x -> x.toUpperCase()).forEach(System.out::print);}
}

我们可以看到,用map操作转换成大写功能生效了,但是用peek操作却没有生效。peek只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。

终止操作:

1.forEach

遍历操作,包含:forEach 和 forEachOrdered

forEach:支持并行处理

forEachOrdered:是按顺序处理的,遍历速度较慢

public class ForEachTest {@Testpublic void testForEach() {List<String> list = Lists.newArrayList("a", "b", "ab");//结果:a b ablist.stream().forEach(x-> System.out.print(x+' '));System.out.println("");//可以简化//结果:a b ablist.forEach(x-> System.out.print(x+' '));System.out.println("");//结果:a b ablist.stream().forEachOrdered(x-> System.out.print(x+' '));}
}

2.collect

收集操作,将所有的元素收集起来,Collectors 提供了非常多收集器。包含:toMap、toSet、toList、joining,groupingBy,maxBy,minBy等操作。

toMap:将数据流转换为map,里面包含的元素是用key/value的形式的

toSet:将数据流转换为set,里面包含的元素不可重复

toList:将数据流转出为list,里面包含的元素是有序的

joining:拼接字符串

groupingBy:分组,可以将list转换map

couting:统计元素数量

maxBy:获取最大元素

minBy:获取最小元素

summarizingInt: 汇总int类型的元素,返回IntSummaryStatistics,再调用具体的方法对元素进行统计:getCount(统计数量),getSum(求和),getMin(获取最小值),getMax(获取最大值),getAverage(获取平均值)

summarizingLong:汇总long类型的元素,用法同summarizingInt

summarizingDouble:汇总double类型的元素,用法同summarizingInt

averagingInt:获取int类型的元素的平均值,返回一个double类型的数据

averagingLong:获取long类型的元素的平均值,用法同averagingInt

averagingDouble:获取double类型的元素的平均值,用法同averagingInt

mapping:获取映射,可以将原始元素的一部分内容作为一个新元素返回

public class CollectTest {@Data@AllArgsConstructorclass User {private String name;private Integer age;}@Testpublic void testCollect() {List<String> list0 = Lists.newArrayList("a", "b", "ab");Map<String, String> collect0 = list0.stream().collect(Collectors.toMap(String::new, Function.identity()));//结果:{ab=ab, a=a, b=b}System.out.println(collect0);List<String> list = Lists.newArrayList("a", "b", "ab", "a", "b", "ab");List<String> collect1 = list.stream().collect(Collectors.toList());//结果:[a, b, ab, a, b, ab]System.out.println(collect1);//结果:[a, ab, b]Set<String> collect2 = list.stream().collect(Collectors.toSet());System.out.println(collect2);String collect3 = list.stream().collect(Collectors.joining(","));//结果:a,b,ab,a,b,abSystem.out.println(collect3);Map<String, List<String>> collect4 = list.stream().collect(Collectors.groupingBy(Function.identity()));//结果:{ab=[ab, ab], a=[a, a], b=[b, b]}System.out.println(collect4);Long collect = list.stream().collect(Collectors.counting());//结果:6System.out.println(collect);String collect5 = list.stream().collect(Collectors.maxBy((a, b) -> a.compareTo(b))).orElse(null);//结果:bSystem.out.println(collect5);String collect6 = list.stream().collect(Collectors.minBy((a, b) -> a.compareTo(b))).orElse(null);//结果:aSystem.out.println(collect6);List<String> list2 = Lists.newArrayList("2", "3", "5");IntSummaryStatistics summaryStatistics = list2.stream().collect(Collectors.summarizingInt(x -> Integer.parseInt(x)));long sum = summaryStatistics.getSum();//结果:10System.out.println(sum);Double collect7 = list2.stream().collect(Collectors.averagingInt(x -> Integer.parseInt(x)));//结果:3.3333333333333335System.out.println(collect7);List<User> userList = new ArrayList<User>() {{add(new User("jack",23));add(new User("james",30));add(new User("curry",28));}};List<String> collect8 = userList.stream().collect(Collectors.mapping(User::getName, Collectors.toList()));//[jack, james, curry]System.out.println(collect8);}
}

3.find

查找操作,包含:findFirst、findAny

findFirst:找到第一个,返回的类型为Optional

findAny:使用 stream() 时找到的是第一个元素,使用 parallelStream() 并行时找到的是其中一个元素,返回的类型为Optional

public class FindOpTest {@Testpublic void testFindOp() {List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "bc", "ab");//查找第一匹配的元素String data1 = list.stream().findFirst().orElse(null);//结果: aSystem.out.println(data1);String data2 = list.stream().findAny().orElse(null);//结果: aSystem.out.println(data2);}
}

4.match

匹配操作,包含:allMatch、anyMatch、noneMatch

allMatch:所有元素都满足条件,返回boolean类型

anyMatch:任意一个元素满足条件,返回boolean类型

noneMatch:所有元素都不满足条件,返回boolean类型

public class MatchTest {@Testpublic void testMatch() {List<Integer> list = Lists.newArrayList(2, 3, 5, 7);boolean allMatch = list.stream().allMatch(x -> x > 1);//结果:trueSystem.out.println(allMatch);boolean allMatch2 = list.stream().allMatch(x -> x > 2);//结果:falseSystem.out.println(allMatch2);boolean anyMatch = list.stream().anyMatch(x -> x > 2);//结果:trueSystem.out.println(anyMatch);boolean noneMatch1 = list.stream().noneMatch(x -> x > 5);//结果:falseSystem.out.println(noneMatch1);boolean noneMatch2 = list.stream().noneMatch(x -> x > 7);//结果:trueSystem.out.println(noneMatch2);}
}

5.count

统计操作,效果跟调用集合的size()方法类似

public class CountOpTest {@Testpublic void testCountOp() {List<String> list = Lists.newArrayList("a", "b", "ab");long count = list.stream().count();//结果:3System.out.println(count);}
}

6.min、max

min:获取最小值,返回Optional类型的数据

max:获取最大值,返回Optional类型的数据

public class MaxMinTest {@Testpublic void testMaxMin() {List<Integer> list = Lists.newArrayList(2, 3, 5, 7);Optional<Integer> max = list.stream().max((a, b) -> a.compareTo(b));//结果:7System.out.println(max.get());Optional<Integer> min = list.stream().min((a, b) -> a.compareTo(b));//结果:2System.out.println(min.get());}
}

7.reduce

规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。

reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。

public class ReduceTest {@Testpublic void testReduce() {List<Integer> list = Lists.newArrayList(2, 3, 5, 7);Integer sum1 = list.stream().reduce(0, Integer::sum);//结果:17System.out.println(sum1);Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);//结果:17System.out.println(reduce.get());Integer max = list.stream().reduce(0, Integer::max);//结果:7System.out.println(max);Integer min = list.stream().reduce(0, Integer::min);//结果:0System.out.println(min);Optional<Integer> reduce1 = list.stream().reduce((a, b) -> a > b ? b : a);//2System.out.println(reduce1.get());}
}

8.toArray

数组操作,将数据流的元素转换成数组。

public class ArrayTest {@Testpublic void testArray() {List<String> list = Lists.newArrayList("a", "b", "ab");String[] strings = list.stream().toArray(String[]::new);//结果:a b abfor (int i = 0; i < strings.length; i++) {System.out.print(strings[i]+" ");}}
}

stream和parallelStream的区别
stream:是单管道,称其为流,其主要用于集合的逻辑处理。

parallelStream:是多管道,提供了流的并行处理,它是Stream的另一重要特性,其底层使用Fork/Join框架实现

public class StreamTest {@Testpublic void testStream() {List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);//结果:1234567list.stream().forEach(System.out::print);}
}
public class ParallelStreamTest {@Testpublic void testParallelStream() {List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);//结果:5726134list.parallelStream().forEach(System.out::print);}
}

我们可以看到直接使用parallelStream的forEach遍历数据,是没有顺序的。

如果要让parallelStream遍历时有顺序怎么办呢?

public class ParallelStreamTest {@Testpublic void testParallelStream() {List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);//结果:1234567list.parallelStream().forEachOrdered(System.out::print);}
}

parallelStream的工作原理:

实际工作中的案例
1.从两个集合中找相同的元素。一般用于批量数据导入的场景,先查询出数据,再批量新增或修改。

public class WorkTest {@Testpublic void testWork1() {List<String> list1 = Lists.newArrayList("a", "b", "ab");List<String> list2 = Lists.newArrayList("a", "c", "ab");List<String> collect = list1.stream().filter(x -> list2.stream().anyMatch(e -> e.equals(x))).collect(Collectors.toList());//结果:[a, ab]System.out.println(collect);}
}

2.有两个集合a和b,过滤出集合a中有,但是集合b中没有的元素。这种情况可以使用在假如指定一个id集合,根据id集合从数据库中查询出数据集合,再根据id集合过滤出数据集合中不存在的id,这些id就是需要新增的。

@Test
public void testWork2() {List<String> list1 = Lists.newArrayList("a", "b", "ab");List<String> list2 = Lists.newArrayList("a", "c", "ab");List<String> collect = list1.stream().filter(x -> list2.stream().noneMatch(e -> e.equals(x))).collect(Collectors.toList());//结果:[b]System.out.println(collect);
}

3.根据条件过滤数据,并且去重做数据转换

@AllArgsConstructor
@Data
class User {private String name;private Integer age;
}@Test
public void testWork3() {List<User> userList = new ArrayList<User>() {{add(new User("jack",23));add(new User("james",30));add(new User("curry",28));add(new User("tom",27));add(new User("sue",29));}};List<String> collect = userList.stream().filter(x -> x.getAge() > 27).sorted((a, b) -> a.getAge().compareTo(b.getAge())).limit(2).map(User::getName).collect(Collectors.toList());//结果:[curry, sue]System.out.println(collect);
}

4.统计指定集合中,姓名相同的人中年龄最小的年龄

  @Testpublic void testWork4() {List<User> userList = new ArrayList<User>() {{add(new User("tom", 23));add(new User("james", 30));add(new User("james", 28));add(new User("tom", 27));add(new User("sue", 29));}};userList.stream().collect(Collectors.groupingBy(User::getName)).forEach((name, list) -> {User user = list.stream().sorted((a, b) -> a.getAge().compareTo(b.getAge())).findFirst().orElse(null);//结果:name:sue,age:29//     name:tom,age:23//     name:james,age:28System.out.println("name:" + name + ",age:" + user.getAge());});}

【java】java 8 的一些集合操作相关推荐

  1. 好用java库(二) : lambdaj (集合操作)

    接着介绍另外一个好用的java库. 记得之前做过一个web services,业务逻辑是很简单,可是代码写得多又长,因为基本上都是在对ArrayList结果进行各种筛选,排序,聚合等操作.大家都有这样 ...

  2. java集合操作-----求两个集合的交集和并集

    java求两个集合的交集和并集 java如何求两个集合的交集和并集呢??其实java的API中已经封装了方法.今天写个简单的例子测试一下:(例子中以java.util.LinkedList为例) 求连 ...

  3. java 时间段求并集,java集合操作-----求两个集合的交集和并集

    2019独角兽企业重金招聘Python工程师标准>>> java求两个集合的交集和并集 java如何求两个集合的交集和并集呢??其实java的API中已经封装了方法.今天写个简单的例 ...

  4. Java基础知识回顾之四 ----- 集合List、Map和Set

    前言 在上一篇中回顾了Java的三大特性:封装.继承和多态.本篇则来介绍下集合. 集合介绍 我们在进行Java程序开发的时候,除了最常用的基础数据类型和String对象外,也经常会用到集合相关类. 集 ...

  5. Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介

    1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...

  6. Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)

    文章目录 Pre 概述 fail-safe的容器-CopyOnWriteArrayList add remove函数 例子 缺陷 使用场景 Pre Java - Java集合中的快速失败Fail Fa ...

  7. Java - Java集合中的快速失败Fail Fast 机制

    文章目录 什么是 fail-fast 源码解读 Itr 为什么对集合的结构进行修改会发生并发修改异常-源码分析 修改方法之 remove 修改方法之 add 案例分享 [案例一] [案例二] [案例三 ...

  8. java hashset 源码_Java集合源码分析-HashSet和LinkedHashSet

    前两篇文章分别分析了Java的ArrayList和LinkedList实现原理,这篇文章分析下HashSet和LinkedHashSet的源码.重点讲解HashSet,因为LinkedHashSet是 ...

  9. JAVA程序员面试题集合

    JAVA程序员面试题集合 分类: 编程语言 2012-12-08 12:10 50人阅读 评论(0) 收藏 举报 1.面向对象的特征有哪些方面 (1)抽象: 抽象就是忽略一个主题中与当前目标无关的那些 ...

  10. Java基础知识(数据类型和集合)

    一.数据类型 包装类型 包装类型是对基本数据类型不足之处的补充. 基本数据类型的传递方式是值传递,而包装类型是引用传递,同时提供了很多数据类型间转换的方法. Java1.5 以后可以自动装箱和拆箱 二 ...

最新文章

  1. 【机器视觉案例】(9) AI视觉,手势控制电脑键盘,附python完整代码
  2. 看完这些能控制大脑的寄生虫,你会怀疑人类!
  3. linux字符串转为二进制,Linux printf将十进制转换为二进制?
  4. 大一计算机论文_大一计算机实验报告
  5. python及pip中常用命令,经常总结
  6. 转:深度学习与自然语言处理之五:从RNN到LSTM
  7. 发布CodeBuild.Net代码自动生成器 V2008 2.01(Vs2008)和架构实例源码Demo
  8. cJSON各函数实现的功能
  9. 汇编@data_汇编语言(4)--内存段
  10. 兄弟连教育分享:用CSS实现鼠标悬停提示的方法
  11. vue项目本地服务器调用豆瓣接口,vue调用豆瓣API加载图片403问题
  12. 手写reduce方法
  13. OfficeXP的激活码保存位置
  14. java解析20万Excel
  15. UINO优锘:数据中心可视化管理面向运营的IT运维配置管理
  16. 系统自带输入法➋➌➍➎➏➐➑➒问题
  17. 久邦数码(3G门户)面试
  18. 并发与同步、信号量与管程、生产者消费者问题
  19. 浏览器被hao123劫持首页处理
  20. 解决雷电模拟器上Xposed框架不能用的问题。

热门文章

  1. 理想汽车4月交付5539辆 累计交付51715辆
  2. 网易北京:全员核酸检测为阴性 园区环境检测为阴性
  3. 月球好忙 蓝色起源2024年要将首位女性送上月球表面
  4. 《唐人街探案3》回归春节档,2021大年初一上映!
  5. 台积电今年9月营收达302.1亿元 同比增长24.9%
  6. 某自媒体发布“抢小孩”视频,最高近25万次点赞,结果竟是自导自演!
  7. 标配65W闪充!865旗舰充电3分钟“满血复活”
  8. 富士康工业互联:生产口罩优先用于内部防疫 预计对经营业绩影响较小
  9. 小米5G手机重回1999元,还需要等其它的5G手机吗?
  10. 中国电信9月将率先推出5G新号段:资费也随之曝光 最高599元/月?