引入流

Stream API的代码

  • 声明性 更简洁,更易读
  • 可复合 更灵活
  • 可并行 性能更好

流是什么?

  • 它允许以声明方式处理数据集合
  • 遍历数据集的高级迭代器
  • 透明地并行处理
  • 简短定义:从支持数据处理操作的源生成的元素序列
  • 特点:流水线和内部迭代

返回低热量菜肴的名称

List<Dish> menu = FakeDb.getMenu();

List<String> lowCaloricDishesName =
// menu.stream()
menu.parallelStream() // 并行处理
.filter(d -> d.getCalories() < 400) // 筛选低卡路里
.sorted(comparing(Dish::getCalories)) // 按卡路里排序
.map(Dish::getName) // 提取菜肴名称
.collect(toList()); // 返回list System.out.println(lowCaloricDishesName);

流与集合?

  • 流只能遍历一次
  • 流是内部迭代,集合是外部迭代

流的操作?

  • 流的使用:一个数据源,一个中间操作链,一个终端操作

  • 流的操作有两类:中间操作与终端操作

使用流

筛选、切片

  • 谓词筛选 filter
  • 去重 distinct
  • 截短流 limit
  • 跳过元素 skip
// 筛选前2个素菜
FakeDb.getMenu().stream()
.filter(d -> d.isVegetarian()) // 这就是谓词筛选
.limit(2) // 截断流,返回前n个元素
.forEach(System.out::println); Arrays.asList(1, 2, 2, 3, 3, 3).stream()
.distinct() // 去重
.skip(1) // 跳过元素
.forEach(System.out::println);

映射

  • 对每个元素应用函数 map
  • 扁平化 flatMap 把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流
// 提取菜名
FakeDb.getMenu().stream()
.map(Dish::getName)
.forEach(System.out::println); // 求每个字符串长度
Arrays.asList("Java 8", "Lambdas", "In", "Action").stream()
.map(String::length)
.forEach(System.out::println);

流的扁平化 flatMap

单词列表返回字符列表

// 单词列表返回字符列表 如输入列表["Hello", "World"],输出列表["H", "e", "l", "o", "W", "r", "d"]
// 尝试一:不成功
Arrays.asList("Hello", "World").stream()
.map(w -> w.split("")) // 将每个单词转为字符数组 Stream<String[]>
.distinct()
.forEach(System.out::println);

Arrays.stream()可以将数组转换成流

// 尝试二:不成功
Arrays.asList("Hello", "World").stream()
.map(w -> w.split("")) // 将每个单词转为字符数组 Stream<String[]>
.map(Arrays::stream) // 将每个数组转换成一个单独的流 Stream<Stream<String>>
.distinct()
.forEach(System.out::println);

使用flatMap

Arrays.asList("Hello", "World").stream()
.map(w -> w.split("")) // 将每个单词转为字符数组
.flatMap(Arrays::stream)// 将各个生成流扁平化为单个流 Stream<String>
// 各个数组并不是分别映射成一个流,而是映射成流的内容
.distinct()
.forEach(System.out::println);

flatMap方法让你先把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流

flatMap使用举例

// 数字列表的平方列表
Arrays.asList(1, 2, 3, 4, 5, 6).stream()
.map(n -> n * n)
.forEach(System.out::println);
System.out.println("----------------------");
// 两个数组列表的数对
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
numbers1.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.forEach(a -> System.out.println(a[0] + "\t" + a[1]));
System.out.println("----------------------");
// 总和能被3整除的数对
numbers1.stream()
.flatMap(i -> numbers2.stream()
.filter(j -> (i + j) % 3 == 0)
.map(j -> new int[]{i, j}))
.forEach(a -> System.out.println(a[0] + "\t" + a[1]));

查找和匹配

从单词列表中返回所用到的字符

allMatch/anyMatch/noneMatch/findFirst/findAny

匹配

// 至少匹配一个元素
boolean match = FakeDb.getMenu().stream()
.anyMatch(Dish::isVegetarian);
System.out.println(match); // 匹配所有元素
match = FakeDb.getMenu().stream()
.allMatch(d -> d.getCalories() > 1000);
System.out.println(match); // 所有都不匹配
match = FakeDb.getMenu().stream()
.noneMatch(d -> d.getCalories() > 1000);
System.out.println(match);

查找

Optional<Dish> dish = FakeDb.getMenu().stream()
.filter(Dish::isVegetarian)
.findAny(); // 关于返回值 Optional<T> 的用法:
System.out.println(dish.isPresent()); // 存在返回true,否则返回false
dish.ifPresent(System.out::println); // 存在就执行代码块
System.out.println(dish.get()); // 存在返回值,不存在抛异常
Dish defaultDish = new Dish("pork", false, 800, Dish.Type.MEAT);
System.out.println(dish.orElse(defaultDish)); // 存在返回值,否则返回默认值 // 查找第一个元素
Optional<Dish> dish2 = FakeDb.getMenu().stream()
.filter(Dish::isVegetarian)
.findFirst();

归约

// 终端操作的返回值
// allMatch boolean
// forEach void
// findAny Optional<T>
// collect R eg:List<T> List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 元素求和,有初始值的情况
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println(sum); // 元素求和,无初始值
Optional<Integer> sum2 = numbers.stream()
.reduce(Integer::sum);
System.out.println(sum2.get()); // 最大值
int maxValue = numbers.stream().reduce(0, Integer::max);
System.out.println(maxValue); // 最小值
int minValue = numbers.stream().reduce(0, Integer::min);
System.out.println(minValue); // 菜单中菜的个数
int dishCount = FakeDb.getMenu().stream()
.map(d -> 1)
.reduce(0, (a, b) -> a + b);
System.out.println(dishCount);
// 内置count
System.out.println(FakeDb.getMenu().stream().count());

无状态操作:诸如map或filter等操作会从输入流中获取每一个元素,并在输出流中得到0或1个结果。

有状态操作:从流中排序和删除重复项时都需要知道先前的历史

归约应用举例

List<Transaction> tList = FakeDb.getTransactions();
// (1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。
List<Transaction> list01 = tList.stream()
.filter(t -> t.getYear() == 2011) // 2011年的交易
.sorted(Comparator.comparing(Transaction::getValue)) // 交易额从低到高
.collect(Collectors.toList());
System.out.println(list01); // (2) 交易员都在哪些不同的城市工作过?
List<String> cityList = tList.stream()
.map(t -> t.getTrader().getCity()) // 交易员所在城市
.distinct() // 去重
.collect(Collectors.toList());
System.out.println(cityList); // (3) 查找所有来自于剑桥的交易员,并按姓名排序。
List<Trader> list3 = tList.stream()
.map(Transaction::getTrader) // 交易员
.filter(t -> "Cambridge".equals(t.getCity())) // 来自剑桥
.distinct() // 去重
.sorted(Comparator.comparing(Trader::getName)) // 按姓名排序
.collect(Collectors.toList());
System.out.println(list3); // (4) 返回所有交易员的姓名字符串,按字母顺序排序。
String nameStr = tList.stream()
.map(t -> t.getTrader().getName()) // 交易员的姓名
.distinct() // 去重
// .sorted(Comparator.comparing(String::toString)) // 排序
.sorted() // 排序,可以简写
// .collect(Collectors.toList());
// .reduce("", (n1, n2) -> n1 + n2 + " "); // 这种写法效率不高
.collect(Collectors.joining(" "));
System.out.println(nameStr); // (5) 有没有交易员是在米兰工作的?
// Optional<String> optional5 = tList.stream()
// .map(Transaction::getTrader)
// .map(Trader::getCity)
// .filter(city -> "Milan".equals(city))
// .findAny();
// System.out.println(optional5.isPresent());
boolean milanBased = tList.stream()
.anyMatch(t -> t.getTrader().getCity().equals("Milan"));
System.out.println(milanBased); // (6) 打印生活在剑桥的交易员的所有交易额。
// Optional<Integer> optional6 = tList.stream()
// .filter(t -> "Cambridge".equals(t.getTrader().getCity()))
// .map(Transaction::getValue)
// .reduce((a, b) -> a + b);
// System.out.println(optional6.get());
tList.stream()
.filter(t -> "Cambridge".equals(t.getTrader().getCity()))
.map(Transaction::getValue)
.forEach(System.out::println); // (7) 所有交易中,最高的交易额是多少?
Optional<Integer> optional7 = tList.stream()
.map(Transaction::getValue)
.reduce(Integer::max);
System.out.println(optional7.get()); // (8) 找到交易额最小的交易。
Optional<Integer> optional8 = tList.stream()
.map(Transaction::getValue)
.reduce(Integer::min);
System.out.println(optional8.get());

数值流与对象流

// 计算菜单中的卡路里
int calories = FakeDb.getMenu().stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
// 它有一个暗含的装箱成本,每个Integer都必须拆箱成一个原始类型,再进行求和
System.out.println(calories); // 原始类型流特化
// IntStream,DoubleStream,LongStream分别将流中的元素特化为int,long,double,从而避免暗含的装箱成本 // 映射到数值流
int sum = FakeDb.getMenu().stream() // 返回 Stream<Dish>
.mapToInt(Dish::getCalories) // 返回 IntStream
.sum(); // sum,max,min,average
// sum,如果流是空的,sum默认返回0
System.out.println(sum); // 转换回对象流
FakeDb.getMenu().stream()
.mapToInt(Dish::getCalories)
.boxed(); // 转换为Stream<Integer> // 默认值OptionalInt OptionalDouble OptionalLong
// sum有默认值0,max,min,average没有默认值
OptionalInt maxCalories = FakeDb.getMenu().stream()
.mapToInt(Dish::getCalories)
.max();
System.out.println(sum);

数值范围

// [1, 100)
IntStream numbers = IntStream.range(1, 100);
// [1 100]
IntStream numbers2 = IntStream.rangeClosed(1, 100); // 求勾股数
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100)
.filter(b -> Math.sqrt(a*a + b*b )%1==0)
.mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a * a + b * b)}))
.forEach(a -> System.out.println(a[0] + ", " + a[1] + ", " + a[2])); // 求勾股数 优化
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100)
.mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)}))
.filter(t -> t[2] % 1 == 0)
.forEach(a -> System.out.println(a[0] + ", " + a[1] + ", " + a[2]));

生成流

// 由值创建流
Stream.of("java 8", "Lambdas", "In", "Action")
.map(String::toUpperCase)
.forEach(System.out::println); // 由数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
System.out.println(sum); // 由文件生成流:统计文件中的不同单词数
long uniqueWords = 0;
try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){ uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count(); } catch (IOException e) {
e.printStackTrace();
};
System.out.println(uniqueWords); // 由函数生成流 无限流
// Stream.iterate
// Stream.generate
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println); // 迭代:斐波纳契元组数列
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]})
.limit(20)
.forEach(t -> System.out.println("(" + t[0] + ", " + t[1] + ")")); // 生成
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println); // 生成一个全是1的无限流
IntStream.generate(() -> 1).limit(11).forEach(System.out::println); // 生成:斐波纳契元组数列
IntStream.generate(new IntSupplier() {
private int previous = 0;
private int current = 1;
@Override
public int getAsInt() {
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
}).limit(11).forEach(System.out::println);

其他,备注

遍历数组取索引值

https://stackoverflow.com/questions/18552005/is-there-a-concise-way-to-iterate-over-a-stream-with-indices-in-java-8

https://www.zhihu.com/question/51841706

遍历数组取索引值

// import static java.util.stream.Collectors.toList;

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(toList()); // List<String> // 方案一:
IntStream.range(0, names.length)
.forEach(i -> System.out.println(i + ", " + names[i])); // 方案二:
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
.filter(n -> n.length() <= index.incrementAndGet())
.collect(Collectors.toList()); // 方案三:使用Guava
Streams.mapWithIndex(Arrays.stream(names)
,(str, index) -> index + ", " + str)
.forEach(System.out::println); // 方案四:未看懂
Seq.seq(Stream.of(names)).zipWithIndex()
.filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
.toList(); // 方案五:未看懂
LazyFutureStream.of(names)
.zipWithIndex()
.filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
.toList();
// 方案六:
int[] idx = {-1};
Arrays.stream(names)
.forEach(i -> System.out.println(++idx[0] + ", " + names[idx[0]]));

第三方库

Guava

Apache

lambdaj

Joda-Time

流,用声明性的方式处理数据集 - 读《Java 8实战》的更多相关文章

  1. 读书笔记,《Java 8实战》,第四章,引入流

       集合是Java中使用最多的API,但集合操作却远远算不上完美.主要表现在两点,    第一,集合不能让我们像数据库的SQL语言一样用申明式的语言指定操作:    第二,现在的集合API无法让我们 ...

  2. KBMMW SampleService/SampleClient方式传输数据集

    马上周末了,趁着下午这会儿回顾一下这几天对旧项目的升级过程,一些重要但不常用的东西记录下来是很有必要的.其中一个项目中对KBMMW的远程数据通讯方式做了改进,利用SampleService/Sampl ...

  3. BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。面向切面 将声明性事务管理集成到应用程序中

    Spring 系列: Spring 框架简介 https://www.ibm.com/developerworks/cn/java/wa-spring1/ Spring 框架简介 Spring AOP ...

  4. 声明对象的方式/构造函数/原型/this指向

      函数的发展历程(声明函数的方式):     1.通过Object构造函数或字面量的方式创建单个对象 var obj = new Object; obj.name="新华"; o ...

  5. 瀑布流的三种实现方式(原生js+jquery+css3)

    前言 项目需求要弄个瀑布流的页面,用的是waterfall这个插件,感觉还是可以的,项目赶就没自己的动手写.最近闲来没事,就自己写个.大致思路理清楚,还是挺好实现的... 原生javascript版 ...

  6. javascript两种声明函数的方式的一次深入解析

    声明函数的方式 javascript有两种声明函数的方式,一个是函数表达式定义函数,也就是我们说的匿名函数方式,一个是函数语句定义函数,下面看代码: /*方式一*/ var FUNCTION_NAME ...

  7. 数据库事务隔离级别+Spring 声明性事务隔离级别

    数据库事务隔离级别 数据库提供了四种事务隔离级别, 不同的隔离级别采用不同的锁类开来实现. 在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低. 大多数数 ...

  8. 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?

    写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...

  9. CUDA 7 Stream流简化并发性

    CUDA 7 Stream流简化并发性 异构计算是指高效地使用系统中的所有处理器,包括 CPU 和 GPU .为此,应用程序必须在多个处理器上并发执行函数. CUDA 应用程序通过在 streams  ...

随机推荐

  1. 记一次 Nuxt 3 在 Windows 下的打包问题

    0. 背景 之前用 Nuxt 3 写了公司的官网,包括了样式.字体图标.图片.视频等,其中样式和字体图标放在了 assets/styles 和 assets/fonts 目录下,而图片和视频则放在了 ...

  2. #define的3种用法详解

    1.#define 的作用 在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为"宏".被定义为"宏"的标识符称为"宏名".在编译 ...

  3. Qunee for HTML5 v1.6新版本发布

    Qunee for HTML5 V1.6正式发布,修复了一些 BUG,增加了滚动条支持,改进了编辑器,增加了JSON 导入导出.告警冒泡.连线流动,UI 定制等扩展示例,欢迎 访问 导航面板 增加了滚 ...

  4. 【小程序开发】 点击button按钮,引导用户授权

    一. 前言 小程序官方文档,上面说明 wx.getUserInfo(OBJECT) 注意:此接口有调整,使用该接口将不再出现授权弹窗,请使用 <button open-type="ge ...

  5. SpringBoot快速入门(解析+入门案例源码实现)

    这里写目录标题 SpringBoot入门 一.SpringBoot 概念 二.JavaConfig 入门 1. JavaConfig 概念 2. 项目准备 三.常用注解 四.SpringBoot 入门 ...

  6. git clone 遇到的问题

    经常遇到这个问题, 所以今天决定把它记录下来 当使用git clone时,会报Please make sure you have the correct access rights and the r ...

  7. swig模板引擎和ejs模板引擎

    swig模板引擎的基本用法: 1. 变量 {{ name }}  //name名前后必须要加空格,不加就会报错 2. 属性 {{ student.name }} 3. 模板继承 swig使用exten ...

  8. Ubuntu安装docker(摘自官网,自用)

    在 Ubuntu 上安装 Docker 引擎(按照标红顺序执行命令) 预计阅读时间:11分钟 适用于 Linux 的 Docker 桌面 Docker Desktop 可帮助您在 Mac 和 Wind ...

  9. xpath 获取meta里的keywords及description的方法

    html中的head的meta元素中的keywords与description标签如下: <meta name="keywords" content="xxxx&q ...

  10. Java学习day21

    今天学习了弹窗,除了此前学的按键以外,弹窗也是程序中广泛使用到的一个方面 做了一个简单的弹窗 除了按键以外,有时候我们需要在界面上显示更多的内容,甚至是图片等,这个时候就需要使用到标签 通过标签和Ic ...