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<Integer> integerStream = Stream.of(1, 2, 3, 5);
// generate, 无限长度, 懒加载, 类似工厂, 使用必须指定长度
Stream<Double> 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()
    @Test
public void test2() {
List<Integer> integers = Arrays.asList(, , , , , );
Stream<Integer> stream = integers.stream();
}

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

  • java.io.BufferedReader.lines()
  • Pattern.splitAsStream(java.lang.CharSequence)
  • java.util.stream.IntStream.range()

4), 自定义supplier接口

    @Test
public void test13() {
Stream.generate(new PersonSupplier()).
limit().
forEach(p -> System.out.println(p.getName() + ", " + p.getAge())); }
private class PersonSupplier implements Supplier<Person> {
private int index = ;
private Random random = new Random();
@Override
public Person get() {
return new Person(index++, "StormTestUser" + index, random.nextInt());
}
}

流的主要操作( 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保持不变

. distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;
. filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;
. map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。
有 mapToInt, mapToLong, mapToDouble
直接转换为响应的类型, 避免拆装箱的消耗
. flatMap:和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;
. peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;
. limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
. skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
8. range: 截取
9, sorted: 排序

排序使用:

    @Test
public void test11() {
List<String> list = Arrays.asList("", "", "", "", "", "", "", "", "");
List<String> list2 = list.stream().distinct().sorted((o1, o2) -> (Integer.parseInt(o2) - Integer.valueOf(o1))).collect(Collectors.toList());
System.out.println(list2);
}

综合:

    @Test
public void test3() {
List<Integer> integers = Arrays.asList(, , , null, , null, , , , , );
System.out.println(integers.stream().filter(num -> num != null)
.distinct()
.mapToInt(num -> num * )
.peek(System.out::println).skip().limit().sum());
}

关于多次stream的性能问题:

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

流转换其他数据结构

// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> 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> R collect(Supplier<R> supplier, <R, ? super T> accumulator, <R, R> combiner);

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

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

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

    @Test
public void test4() {
List<Integer> nums = Arrays.asList(, , , null, , null, , , , , );
List<Integer> numsWithCollect = nums.stream().filter(num -> num != null)
.collect(() -> new ArrayList<Integer>(),
(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 )

    @Test
public void test5() {
List<Integer> nums = Arrays.asList(, , , null, , null, , , , , );
List<Integer> collect = nums.stream().filter(num -> num != null)
.collect(Collectors.toList());
System.out.println(collect);
}

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

    // Accumulate names into a List
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList()); // Accumulate names into a TreeSet
Set<String> 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<Department, List<Employee>> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment)); // Compute sum of salaries by department
Map<Department, Integer> totalByDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary))); // Partition students into passing and failing
Map<Boolean, List<Student>> passingFailing =
students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

2) reduce汇聚

    @Test
public void test6() {
List<Integer> nums = Arrays.asList(, , , null, , null, , , , , );
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<Integer> ints = Lists.newArrayList(,,,,,,,,,);
System.out.println("ints sum is:" + ints.stream().reduce(, (sum, item) -> sum + item));

count()

List<Integer> ints = Lists.newArrayList(,,,,,,,,,);
System.out.println("ints sum is:" + ints.stream().count());

match()

    @Test
public void test7() {
List<Integer> nums = Arrays.asList(, , , null, , null, , , , , );
System.out.println(nums.stream().filter(num -> num != null).allMatch(num -> num < ));
}

max(), min()

    @Test
public void test12() 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) 分组

  按年龄分组

@Test
public void test13() {
Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
limit().
collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
}
}

  按是否成年分组

@Test
public void test13() {
Map<Boolean, List<Person>> 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<Document> collection = mongoDatabase.getCollection(Constance.collection()); MongoIterable<TopicMacEntity> iter = collection.find().map(document -> {
String topic = document.get("topic").toString().toUpperCase();
String mac = document.get("mac").toString().toUpperCase();
return new TopicMacEntity(topic, mac);
});
Map<String, List<TopicMacEntity>> collect = Lists.newArrayList(iter).stream().collect(Collectors.groupingBy(TopicMacEntity::topic));
Map<String, List<String>> 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/

jdk8-lambda-stream的使用的更多相关文章

  1. JDK8中Stream使用解析

    JDK8中Stream使用解析 现在谈及JDK8的新特新,已经说不上新了.本篇介绍的就是Stream和Lambda,说的Stream可不是JDK中的IO流,这里的Stream指的是处理集合的抽象概念『 ...

  2. jdk8 Lambda表达式与匿名内部类比较

    Labmda表达式与匿名内部类 前言 Java Labmda表达式的一个重要用法是简化某些匿名内部类(Anonymous Classes)的写法.实际上Lambda表达式并不仅仅是匿名内部类的语法糖, ...

  3. Java8 新特性 Lambda & Stream API

    目录 Lambda & Stream API 1 Lambda表达式 1.1 为什么要使用lambda表达式 1.2 Lambda表达式语法 1.3 函数式接口 1.3.1 什么是函数式接口? ...

  4. forEach与jdk8中的lambda, Stream

    增强for循环 :forEach 反编译后可以看到实际使用的仍然是Iterator+while遍历的 forEach的优点是写法简单,缺点是不能使用xxx.remove(e)或者iter.remove ...

  5. 【转载】JDK8 特性 stream(),lambda表达式,

    Stream()表达式 虽然大部分情况下stream是容器调用Collection.stream()方法得到的,但stream和collections有以下不同: 无存储.stream不是一种数据结构 ...

  6. 超强的Lambda Stream流操作

    原文:https://www.cnblogs.com/niumoo/p/11880172.html 在使用 Stream 流操作之前你应该先了解 Lambda 相关知识,如果还不了解,可以参考之前文章 ...

  7. JDK8之Stream新特性

    https://www.cnblogs.com/cbxBlog/p/9123106.html /** *JDK8 Stream特性 * Created by chengbx on 2018/5/27. ...

  8. 零基础学习java------21---------动态代理,java8新特性(lambda, stream,DateApi)

    1. 动态代理 在一个方法前后加内容,最简单直观的方法就是直接在代码上加内容(如数据库中的事务),但这样写不够灵活,并且代码可维护性差,所以就需要引入动态代理 1.1 静态代理实现 在讲动态代理之前, ...

  9. Java8 Lambda/Stream使用说明

    一.Stream流1. 流的基本概念 1.1 什么是流?流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合.众所周知,集合操作非常麻烦,若要对集合进行筛选.投影,需要 ...

  10. jdk8系列三、jdk8之stream原理及流创建、排序、转换等处理

    一.为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX ...

随机推荐

  1. 第85讲:Scala中For表达式的强大表现力实战

    今天来学一下scala中的For表达式的用法. package scala.learn case class Persons(name:String,isMale:Boolean,children:P ...

  2. PCI总线目标接口状态机设计

    module state_machine (devsel_l, trdy_l, stop_l, pci_ad_oe,      dts_oe, par_oe, bk_oe, pci_ad_en, hi ...

  3. [mysql] mysql 查询语句收集

    // 1. 筛选出当天的中奖名单            $where = " date_format(f_ctime,'%Y-%m-%d') = current_date()"; ...

  4. 7.Git与项目

    Git简介 Git是目前世界上最先进的分布式版本控制系统 安装 sudo apt-get install git 安装成功后,运行如下命令 git 产生 Linus在1991年创建了开源的Linux, ...

  5. Android-Kotlin-空值处理&字符串比较&常量

    空值处理: [案例一:] 1.Kotlin对控制处理很严格,默认就不能写null,否则编译都不通过: 描述Car汽车对象: package cn.kotlin.kotlin_base01 /** * ...

  6. 用 select 语句实现递归的方法

    with Test_Recursion(Id,ParentId)AS(select Id,ParentId  from [V_KPI_DetailsActivities] where ParentId ...

  7. [leetcode 12] Inter to Roman

    1 题目: Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range fr ...

  8. dotnet core瘦身发布

    需要安装nuget包Microsoft.Packaging.Tools.Trimming然后利用如下命令发布dotnet publish -r win10-x64 -c release --self- ...

  9. 使用Code First建模自引用关系笔记

    原文链接 一.Has方法: A.HasRequired(a => a.B); HasOptional:前者包含后者一个实例或者为null HasRequired:前者(A)包含后者(B)一个不为 ...

  10. 【BZOJ3193】 [JLOI2013]地形生成

    BZOJ3193 [JLOI2013]地形生成 Solution 第一问不是很简单吗? 直接计数就好了. 第二问思考无果看了看hyj神仙的代码,发现可以dp求解. 具体可以看代码(其实主要是我说不清楚 ...