前言

最近在实习,在公司看到前辈的一些代码,发现有很多值得我学习的地方,其中有一部分就是对集合使用Stream流式操作,觉得很优美且方便。所以学习一下Stream流,在这里记录一下。

Stream是什么

Stream是Java 8中出现的新特性,极大增强了集合对象的功能,专注于对集合对象进行方便、高效的聚合操作。另外可以配合Lambda表达式,让代码更加容易理解。另外Stream提供串行和并行两种操作方式,并行操作可以很方便的写出高性能的并发程序。

Stream像是一个高级版本的Iterator,使用Iterator只能显式地遍历一个个元素对其执行某些操作;使用Stream,我们只需要指定对集合包含的元素执行什么操作,例如“只获取性别为男的用户”、“获取每个用户姓名的姓氏”等,Stream会帮我们完成隐式的遍历操作,并转换数据。

与Iterator不同的是,Iterator只能串行操作,每次操作完一个元素再去下一个元素。Stream支持串行、并行操作,Stream的并行操作依赖Java 7的Fork/Join框架(JSR166y)来拆分任务和加速处理过程。

Stream就像是一条流水线,单向,不可回头,只能遍历一次,之后就不能再使用了。

使用一个Stream流,一般分为三个步骤:1. 获取数据源-> 2. 中间操作(Intermediate)-> 3. 终端操作(Terminal)。

中间操作:一个流可以有0或多个中间操作,对数据进行转换、过滤等操作,一个接着一个,这些操作是lazy的,中间操作是还没有开始真正的遍历。

终端操作:一个流只能有一个终端操作,使用终端操作之后就会返回结果,不能再使用这个流了。终端操作时,才真正开始遍历。

在Stream中一个流的多次中间操作不是每一次都进行一次遍历的,中间操作是lazy 的,多个中间操作是最终聚合到终端操作的时候进行的,只进行一次遍历循环。可以理解为每个中间操作被当做一个判断条件加入到终端操作循环中,完成每个元素的数据转换。

下面是一些Stream流操作方法的分类:

Stream流的创建方式

  • 数组

    1. Arrays.stream(T array);
    2. stream.of(array)
  • Collection

    1. Collection.stream()
    2. Collection.parallelStream()
  • BufferedReader

    1. java.io.BufferedReader.lines()
  • 静态工厂

    1. java.util.stream.IntStream.range()
    2. java.nio.file.Files.walk()
  • 自己构建

    1. java.util.Spliterator
  • 其他

    1. Random.ints()
    2. BitSet.stream()
    3. Pattern.splitAsStream(java.lang.CharSequence)
    4. JarFile.stream()

创建示例

//数组
String[] array = new String[]{"1","2","3"};
Arrays.stream(array);
Stream.of(array);
Stream.of(1, 2, 3);
//集合
List<String> list = Arrays.asList(array);
list.stream();
list.parallelStream();
//数值,目前只支持IntStream、LongStream、DoubleStream三种
IntStream.of(new int[]{1,2,3}).forEach(System.out::println);
IntStream.range(1,3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);

流转换为其它数据结构

Stream stream = Stream.of("1","2","3");
//Array
String[] array2=(String[]) stream.toArray(String[]::new);
//Collection
List<String> list1=(List)stream.collect(Collectors.toList());
List<String> list2=(List)stream.collect(Collectors.toCollection(ArrayList::new));
Set set=(Set)stream.collect(Collectors.toSet());
Stack stack=(Stack)stream.collect(Collectors.toCollection(Stack::new));
//String
String str = stream.collect(Collectors.joining()).toString();

经典用法

1. 将一个List中元素的某一属性取出来作为一个list,并做过滤

List<Long> names=  users.stream().filter(Objects::nonNull).map(User::getId).collect(Collectors.toList());
//或者
List<Long> names1=
users.stream().filter(Objects::nonNull).map(u->u.getId()).collect(Collectors.toList());
//遍历list
names.forEach(System.out::println);

2. 将List转换成Map

		//key:id value:name
Map<Long, String> map = users.stream().collect(Collectors.toMap(p -> p.getId(), p -> p.getName()));
//或者,第三个参数表示如果key重复保留k1,舍弃k2。
Map<Long, String> map2 = users.stream().collect(Collectors.toMap(User::getId,User::getName,(k1,k2)->k1));
//key:id value:user
Map<Long, User> map3 = users.stream().collect(Collectors.toMap(p -> p.getId(), p->p));
//遍历map,包括k,v。map.values.forEach()不能遍历Key
map3.forEach((k,v)-> System.out.println("k:v="+k+":"+v));

3. 使用sorted对List排序

//降序,默认是升序
List<User> list=
users.stream().sorted(Comparator.comparing(User::getId).reversed()).collect(Collectors.toList());
//遍历list
list.forEach(System.out::println);

Comparator.comparing(User::getId)表示以id作为排序的数据。

4. 对List分组存入一个Map

		//按照性别分组
Map<String,List<User>> map=users.stream().collect(Collectors.groupingBy(User::getSex));
map.forEach((k,v)-> System.out.println("k:v="+k+":"+v));

5. 使用map转换大写

List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
List<String> list2 = list1.stream().map(String::toUpperCase).collect(Collectors.toList());

6. flatMap和map

		//map
List<String> str = Arrays.asList("a,b,c", "d,e", "f");
List<String[]> list1 = str.stream().map(s -> s.split(",")).collect(Collectors.toList());
list1.forEach(p-> System.out.print(Arrays.toString(p)+","));//[a, b, c],[d, e],[f] //flatMap
List<String> list2 = str.stream().map(s -> s.split(",")).flatMap(Arrays::stream).sorted(Comparator.comparing(p->p.toString()).reversed()).collect(Collectors.toList());
System.out.println(list2);//[f, e, d, c, b, a]

flatMap与map的区别在于 flatMap是将一个流中的每个元素都转成一个个流,flatMap之后得到的是每个流中元素的总的集合,即对每个流进行了二次遍历取出其中的元素,融合到总的集合中。

7. reduce

		//求和 sum=11,第一个参数1为初始值(种子),第二个参数为运算规则(BinaryOperator)。1+1+2+3+4=11
Integer sum = Stream.of(1, 2, 3, 4).reduce(1, Integer::sum);
//concat="ABCD";
String concat = Stream.of("A", "B", "C").reduce("", String::concat);
//求和,reduce方法无初始值,返回类型为Optional,需要调用get()方法取值。
Integer sum2 = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
//取最大值,max=2.0。
Double max = Stream.of(1.0, 2.0, -1.0, 1.5).reduce(Double.MIN_VALUE, Double::max);

optional也是Java 8中的新特性,可以存储null或者实例,有机会再深入讲吧。

8. limit和skip

List<Long> ids=users.stream().map(User::getId).limit(5).skip(2).collect(Collectors.toList());
ids.forEach(System.out::println);//0-9 输出了 2 3 4

limit(5)限制只要前五条,skip(2)跳过前两条。特别注意如果limit和skip配合sorted使用,需先进行limit和skip。

9. anyMatch

		Map<Long, String> map = new HashMap<>();
map.put(1L, "1号");
map.put(2L, "2号");
map.put(3L, "3号");
List<User> list = Lists.newArrayList();
list.add(new User(1L,10,"1号","",""));
list.add(new User(2L,10,"2号","",""));
list.add(new User(3L,10,"3号","",""));
boolean f = false;
//所有都匹配的时候才会返回true
for(Map.Entry<Long,String> entry:map.entrySet()){
f = list.stream().anyMatch(p ->
Objects.equals(entry.getKey(), p.getId())
);
}

以下,补充于20191211

突然想起来面试的时候有几个关于取两个List的交集、并集问题也可以用Stream来解决。特此记录一下。

                List<String> list1 = Lists.newArrayList();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4");
List<String> list2 = Lists.newArrayList();
list2.add("3");
list2.add("4");
list2.add("5");
list2.add("6"); //交集,或使用list1.retainAll(list2);
List<String> intersection=list1.stream().filter(i->list2.contains(i)).collect(Collectors.toList());
System.out.println("交集:");
intersection.forEach(System.out::println);
//差集,或使用list1.removeAll(list2);
List<String> reduce=list1.stream().filter(i->!list2.contains(i)).collect(Collectors.toList());
System.out.println("差集:");
reduce.forEach(System.out::println);
//并集
List<String> listAll = list1;
listAll.addAll(list2);
System.out.println("并集:");
listAll.forEach(System.out::println);
//去重并集,或使用list1.removeAll(list2);list1.addAll(list2);
List<String> listAllDistinct = listAll.stream().distinct().collect(Collectors.toList());
System.out.println("去重并集:");
listAllDistinct.forEach(System.out::println);

使用Stream和使用ArrayList的removeAll、retainAll方法比较,Stream不会改变原来的List。


生命不息,学习不止。还需继续努力。20191210

Java的Stream流式操作的更多相关文章

  1. Java8——Stream流式操作的一点小总结

    我发现,自从我学了Stream流式操作之后,工作中使用到的频率还是挺高的,因为stream配合着lambda表达式或者双冒号(::)使用真的是优雅到了极致!今天就简单分(搬)享(运)一下我对strea ...

  2. Java8中的Stream流式操作 - 入门篇

    作者:汤圆 个人博客:javalover.cc 前言 之前总是朋友朋友的叫,感觉有套近乎的嫌疑,所以后面还是给大家改个称呼吧 因为大家是来看东西的,所以暂且叫做官人吧(灵感来自于民间流传的四大名著之一 ...

  3. Java 之 Stream 流

    Stream流 在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端 一.传统遍历 1.传统集合的多步遍历代码 几乎所有的集合(如 ...

  4. java1.8新特性之stream流式算法

    在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: List& ...

  5. Stream流式编程

    Stream流式编程   Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带来的函数式编程,引入了一个 ...

  6. 《JAVA8开发指南》使用流式操作

    为什么需要流式操作 集合API是Java API中最重要的部分.基本上每一个java程序都离不开集合.尽管很重要,但是现有的集合处理在很多方面都无法满足需要. 一个原因是,许多其他的语言或者类库以声明 ...

  7. 第46天学习打卡(四大函数式接口 Stream流式计算 ForkJoin 异步回调 JMM Volatile)

    小结与扩展 池的最大的大小如何去设置! 了解:IO密集型,CPU密集型:(调优)  //1.CPU密集型 几核就是几个线程 可以保持效率最高 //2.IO密集型判断你的程序中十分耗IO的线程,只要大于 ...

  8. Stream流式计算

    Stream流式计算 集合/数据库用来进行数据的存储 而计算则交给流 /** * 现有5个用户,用一行代码 ,一分钟按以下条件筛选出指定用户 *1.ID必须是偶数 *2.年龄必须大于22 *3.用户名 ...

  9. Java学习:Stream流式思想

    Stream流 Java 8 API添加了一种新的机制——Stream(流).Stream和IO流不是一回事. 流式思想:像生产流水线一样,一个操作接一个操作. 使用Stream流的步骤:数据源→转换 ...

随机推荐

  1. SpringBoot配置多注册中心(yml,properties)

    dubbo-2.6.6 dubbo.config.multiple=true    dubbo.registries.z1.timeout = 5000    dubbo.registries.z1. ...

  2. Fiddler抓包设置

    介绍 Fiddler 在 PC 端和移动端,模拟器抓取数据包 Fiddler抓取PC端数据包: 这里 Fiddler 抓取网页客户端的数据包时,其原理就是在 客户端/浏览器 和 服务器端 之间,加上了 ...

  3. C#进阶系列 ---- 《CLR via C#》

      [C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...

  4. 关于ID命名 一个页面唯一

    1.一般ID在一个区域内必须是唯一的.这样是一个规范而且在IE中使用JS通过ID获取这个对象永远只能获取第一个. 2.js无法找到重复的ID,用js获取时,只能得到第一个ID元素,但,如果不同的区域范 ...

  5. Pandas | 07 函数应用

    要将自定义或其他库的函数应用于Pandas对象,有三个重要的方法,下面来讨论如何使用这些方法.使用适当的方法取决于函数应用于哪个层面(DataFrame,行或列或元素). 表合理函数应用:pipe() ...

  6. 搜索法 | 1091bfs搜索:三维bfs

    首先我们来理解样例输入: 尺寸:3行4列5片   阈值:2 1 1 1 1 1 1 1 1 1 1 1 1 ------- 0 0 1 1 0 0 1 1 0 0 1 1 ------- 0 1 1 ...

  7. PATA1062 Talent and Virtue

    技术要点就是,一个是cmp函数的书写,首先应该分清楚排序关系,然后按照顺序依次排下去. 还有这里有一个巧妙点就是,在结构体中加入了类别这个标签. 学会抽象分类解决,排序比较函数cmp本质工作就是比较结 ...

  8. ZROI 暑期高端峰会 A班 Day1 组合计数

    AGC036F Square Constriants 一定有 \(l_i<p_i\le r_i\). 考虑朴素容斥,枚举每个数是 \(\le l_i\) 还是 \(\le r_i\).对于 \( ...

  9. Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:mav问题

    1.导致问题原因:从装系统,从win7改到win10 由于重装了系统,打开eclipse时,maven验证会出错,点击pom文件,会发现有红色的Cannot read lifecycle mappin ...

  10. DAVID 进行 GO/KEGG 功能富集分析

    何为功能富集分析? 功能富集分析是将基因或者蛋白列表分成多个部分,即将一堆基因进行分类,而这里的分类标准往往是按照基因的功能来限定的.换句话说,就是把一个基因列表中,具有相似功能的基因放到一起,并和生 ...