1, 认识stream(声明式编程)

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator, 原始版本的Iterator,用户只能一个一个的遍历元素并对其执行某些操作;高级版本的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,具体这些操作如何应用到每个元素上,就给Stream就好了!

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程

2, 使用stream的基本过程

1, 创建Stream;

2, 转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(**可以有多次转换**);

3, 对Stream进行聚合(Reduce)操作,获取想要的结果;

3, 创建stream

1), 使用stream静态方法创建

@Test

public void test() {

// of

Stream integerStream = Stream.of(1, 2, 3, 5);

// generate, 无限长度, 懒加载, 类似工厂, 使用必须指定长度

Stream generate = Stream.generate(Math::random);

// iterator方法, 无限长度,

Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::print);

}

2), 通过collection的子类生成

Collection.stream()

Collection.parallelStream()

Arrays.stream(T array) or Stream.of()

@Testpublic voidtest2() {

List integers = Arrays.asList(1, 2, 3, 4, 5, 6);

Stream stream =integers.stream();

}

3), buffer生成 (通过实现 Supplier 接口)

java.io.BufferedReader.lines()

Pattern.splitAsStream(java.lang.CharSequence)

java.util.stream.IntStream.range()

4), 自定义supplier接口

@Testpublic voidtest13() {

Stream.generate(newPersonSupplier()).

limit(10).

forEach(p-> System.out.println(p.getName() + "," +p.getAge()));

}private class PersonSupplier implements Supplier{private int index = 0;private Random random = newRandom();

@Overridepublic Person get() {return new Person(index++, "StormTestUser" + index, random.nextInt(100));

}

}

流的主要操作( https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html )

Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

分类操作

Intermediate:

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

Terminal:

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

Short-circuiting:

anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

4, 转换stream

每次使用的本质, 是创建了一个新的stream, 旧的stream保持不变

1. distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;2. filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;3. map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。

有 mapToInt, mapToLong, mapToDouble

直接转换为响应的类型, 避免拆装箱的消耗4. flatMap:和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;5. peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;6. limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;7. skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

8. range: 截取

9, sorted: 排序

排序使用:

@Testpublic voidtest11() {

List list = Arrays.asList("2", "5", "2", "1", "8", "4", "3", "7", "9");

List list2 = list.stream().distinct().sorted((o1, o2) -> (Integer.parseInt(o2) -Integer.valueOf(o1))).collect(Collectors.toList());

System.out.println(list2);

}

综合:

@Testpublic voidtest3() {

List integers = Arrays.asList(1, 1, 3 , null, 2, null, 3, 4, 5, 8, 10);

System.out.println(integers.stream().filter(num -> num != null)

.distinct()

.mapToInt(num-> num * 10)

.peek(System.out::println).skip(2).limit(4).sum());

}

关于多次stream的性能问题:

转换操作都是lazy的,多个转换操作只会在汇聚操作(见下节)的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在汇聚操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。

流转换其他数据结构

//1. Array

String[] strArray1 = stream.toArray(String[]::new);//2. Collection

List list1 =stream.collect(Collectors.toList());

List list2 = stream.collect(Collectors.toCollection(ArrayList::new));

Set set1=stream.collect(Collectors.toSet());

Stack stack1= stream.collect(Collectors.toCollection(Stack::new));//3. String

String str = stream.collect(Collectors.joining()).toString();

5, 汇聚操作

汇聚操作(也称为折叠)接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果。比如查找一个数字列表的总和或者最大值,或者把这些数字累积成一个List对象。Stream接口有一些通用的汇聚操作,比如reduce()和collect();也有一些特定用途的汇聚操作,比如sum(),max()和count()

1), 可变汇聚, collect, 把输入的元素们累积到一个可变的容器中,比如Collection或者StringBuilder;

R collect(Suppliersupplier,accumulator, combiner);

Supplier supplier是一个工厂函数,用来生成一个新的容器;

BiConsumer accumulator也是一个函数,用来把Stream中的元素添加到结果容器中;

BiConsumer combiner还是一个函数,用来把中间状态的多个结果容器合并成为一个(并发的时候会用到)

@Testpublic voidtest4() {

List nums = Arrays.asList(1, 1, 3 , null, 2, null, 3, 4, 5, 8, 10);

List numsWithCollect = nums.stream().filter(num -> num != null)

.collect(()-> new ArrayList(),

(list, item)->list.add(item),

(list1, list2)->list1.addAll(list2));

System.out.println(numsWithCollect);

}

太繁琐了, 在jdk8 中提供了Collectors工具类, 可以直接实现汇聚( http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html )

@Testpublic voidtest5() {

List nums = Arrays.asList(1, 1, 3 , null, 2, null, 3, 4, 5, 8, 10);

List collect = nums.stream().filter(num -> num != null)

.collect(Collectors.toList());

System.out.println(collect);

}

ps: collectos中提供了大量的方法, 粘贴一段api开头的方法

//Accumulate names into a List

List list =people.stream().map(Person::getName).collect(Collectors.toList());//Accumulate names into a TreeSet

Set set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));//Convert elements to strings and concatenate them, separated by commas

String joined =things.stream()

.map(Object::toString)

.collect(Collectors.joining(","));//Compute sum of salaries of employee

int total =employees.stream()

.collect(Collectors.summingInt(Employee::getSalary)));//Group employees by department

Map>byDept=employees.stream()

.collect(Collectors.groupingBy(Employee::getDepartment));//Compute sum of salaries by department

MaptotalByDept=employees.stream()

.collect(Collectors.groupingBy(Employee::getDepartment,

Collectors.summingInt(Employee::getSalary)));//Partition students into passing and failing

Map> passingFailing =students.stream()

.collect(Collectors.partitioningBy(s-> s.getGrade() >= PASS_THRESHOLD));

2) reduce汇聚

@Testpublic voidtest6() {

List nums = Arrays.asList(1, 1, 3 , null, 2, null, 3, 4, 5, 8, 10);

Integer count= nums.stream().filter(num -> num != null)

.reduce((sum, num)-> sum + num).get();

System.out.println(count);

}

可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:**第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素**。这个方法返回值类型是Optional,这是Java8防止出现NPE的一种可行方法,后面的文章会详细介绍,这里就简单的认为是一个容器,其中可能会包含0个或者1个对象。

可以提供一个初始值, 如果ints为空则直接返回默认值

List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);

System.out.println("ints sum is:" + ints.stream().reduce(0, (sum, item) -> sum + item));

count()

List ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);

System.out.println("ints sum is:" + ints.stream().count());

match()

@Testpublic voidtest7() {

List nums = Arrays.asList(1, 1, 3 , null, 2, null, 3, 4, 5, 8, 10);

System.out.println(nums.stream().filter(num -> num != null).allMatch(num -> num < 8));

}

max(), min()

@Testpublic voidtest12() throws IOException {

BufferedReader br= new BufferedReader(new FileReader("d:\\test.log"));int longest =br.lines().

mapToInt(String::length).

max().

getAsInt();

br.close();

System.out.println(longest);

}

– allMatch:是不是Stream中的所有元素都满足给定的匹配条件

– anyMatch:Stream中是否存在任何一个元素满足匹配条件

-  noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true

– findFirst: 返回Stream中的第一个元素,如果Stream为空,返回空Optional

– noneMatch:是不是Stream中的所有元素都不满足给定的匹配条件

– max和min:使用给定的比较器(Operator),返回Stream中的最大|

3) 分组

按年龄分组

@Testpublic voidtest13() {

Map> personGroups = Stream.generate(newPersonSupplier()).

limit(100).

collect(Collectors.groupingBy(Person::getAge));

Iterator it=personGroups.entrySet().iterator();while(it.hasNext()) {

Map.Entry> persons =(Map.Entry) it.next();

System.out.println("Age" + persons.getKey() + "=" +persons.getValue().size());

}

}

按是否成年分组

@Test

public void test13() {

Map> children = Stream.generate(new PersonSupplier()).

limit(100).

collect(Collectors.partitioningBy(p -> p.getAge() < 18));

System.out.println("Children number: " + children.get(true).size());

System.out.println("Adult number: " + children.get(false).size());

}

一个综合运用的例子:

MongoClient client =getMongoClient();

MongoDatabase mongoDatabase=client.getDatabase(Constance.database());

MongoCollection collection =mongoDatabase.getCollection(Constance.collection());

MongoIterable iter = collection.find().map(document ->{

String topic= document.get("topic").toString().toUpperCase();

String mac= document.get("mac").toString().toUpperCase();return newTopicMacEntity(topic, mac);

});

Map> collect =Lists.newArrayList(iter).stream().collect(Collectors.groupingBy(TopicMacEntity::topic));

Map> resultMap =collect.entrySet().stream()

.collect(Collectors.toMap(Map.Entry::getKey, v-> v.getValue().stream().map(t ->t.mac()).collect(Collectors.toList())));return resultMap;

更多grouping的强大用法:

http://developer.51cto.com/art/201404/435431.htm

原博客:

http://ifeve.com/stream/

lambda stream 循环_jdk8-lambda-stream的使用相关推荐

  1. 当你的Stream遇上Lambda就爱上了,超级无敌酷酷 - 第418篇

    历史文章(累计400+篇文章) <国内最全的Spring Boot系列之一> <国内最全的Spring Boot系列之二> <国内最全的Spring Boot系列之三&g ...

  2. Java8新特性概览——Stream特性,Lambda表达式,函数式接口Function、Predicate、Consumer,方法引用等概述

    概述: Java 8 新特性概述:https://www.ibm.com/developerworks/cn/java/j-lo-jdk8newfeature/index.html JAVA8 十大新 ...

  3. Stream流与Lambda表达式(一) 杂谈

    一.流 转换为数组.集合 package com.java.design.java8.Stream;import org.junit.Test; import org.junit.runner.Run ...

  4. 使用Java8新特性(stream流、Lambda表达式)实现多个List 的笛卡尔乘积 返回需要的List<JavaBean>

    需求分析: 有两个Long类型的集合 : List<Long> tagsIds; List<Long> attributesIds; 现在需要将这两个Long类型的集合进行组合 ...

  5. 【Java从入门到头秃专栏 7】语法篇(六) :Lambda表达式(->) 方法引用(::) stream流

    目录 1 Lambda表达式( -> ) ​ 2 方法引用( :: ) 3 Stream流 接下来介绍的三种语法叫:Lambda表达式 方法引用 stream流,这三种语法的使用要有特定条件,在 ...

  6. comparator接口_8000字长文让你彻底了解 Java 8 的 Lambda、函数式接口、Stream 用法和原理

    我是风筝,公众号「古时的风筝」.一个兼具深度与广度的程序员鼓励师,一个本打算写诗却写起了代码的田园码农! 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在 ...

  7. Lambda、函数式接口、Stream 一次性全给你

    就在今年 Java 25周岁了,可能比在座的各位中的一些少年年龄还大,但令人遗憾的是,竟然没有我大,不禁感叹,Java 还是太小了.(难道我会说是因为我老了?) 而就在上个月,Java 15 的试验版 ...

  8. 函数式编程(Lambda表达式、Optional、Stream流)

    函数式编程(Lambda表达式.Optional.Stream流) 文章目录 函数式编程(Lambda表达式.Optional.Stream流) 一.概述 1. 为什么要学习函数式编程? 2. 函数式 ...

  9. 10000字长文让你了解 Java 8 Lambda、函数式接口、Stream 用法和原理

    一定要看到最后,那是心动的感觉! 我是风筝,公众号「古时的风筝」.一个兼具深度与广度的程序员鼓励师,一个本打算写诗却写起了代码的田园码农!文章会收录在 JavaNewBee 中,更有 Java 后端知 ...

最新文章

  1. Squid故障与解决方法汇总
  2. 在用安全框架前,我想先让你手撸一个登陆认证
  3. 史上最详细版Centos6安装详细教程
  4. java达达租车接口_Java第一个项目——达达租车系统v1
  5. Windows2008应用之配置客户端自动添加打印机
  6. Maven——继承和聚合
  7. kettle创建mysql资源库
  8. 17 - 引用类型比较内容
  9. free bsd x修改UTC-SCT
  10. FineReport帆软学习笔记汇总
  11. Java八大基础数据类型转换
  12. CVE-2018-5767 栈溢出漏洞复现
  13. 国内的9家域名顶级注册商
  14. 剑心---速度与位置
  15. 如何安装java环境_如何安装java环境变量
  16. js判断两个时间是否超过一年
  17. 【网络游戏植入案例】
  18. 用python让excel飞起来(第7章 图表操作)
  19. js之 实现浏览器下载图片保存到本地
  20. linux解压7z文件,Linux下解压.zip.7z和.rar文件

热门文章

  1. nodeName,nodeValue,nodeType,typeof,instanceof 的区别
  2. java基类和派生类圆_java – 当基类和派生类都具有相同名称的变量时会发生什么...
  3. 怎么把git代码导入到本地仓库_git在本地仓库添加了一个tag,如何把这个tag同步到远程仓库?...
  4. android 支付宝 地图,利用百度地图实现支付宝“到位”功能(地图模式)
  5. 零基础学习java------day1------计算机基础以及java的一些简单了解
  6. JavaScript 数据类型梳理
  7. 软件测试 第三次作业
  8. Java中正则表达式、模式匹配与信息抽取
  9. 学习Altas 笔记[js调用重载的方法出错,如何处理]
  10. java插件安装步骤_eclipse插件安装的四种方法