博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
流,用声明性的方式处理数据集 - 读《Java 8实战》
阅读量:6966 次
发布时间:2019-06-27

本文共 10870 字,大约阅读时间需要 36 分钟。

  hot3.png

引入流

Stream API的代码

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

流是什么?

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

返回低热量菜肴的名称

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

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

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

使用flatMap

Arrays.asList("Hello", "World").stream()    .map(w -> w.split("")) // 将每个单词转为字符数组    .flatMap(Arrays::stream)// 将各个生成流扁平化为单个流 Stream
// 各个数组并不是分别映射成一个流,而是映射成流的内容 .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
numbers1 = Arrays.asList(1, 2, 3);List
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 = FakeDb.getMenu().stream() .filter(Dish::isVegetarian) .findAny();// 关于返回值 Optional
的用法:System.out.println(dish.isPresent()); // 存在返回true,否则返回falsedish.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
dish2 = FakeDb.getMenu().stream() .filter(Dish::isVegetarian) .findFirst();

归约

// 终端操作的返回值// allMatch boolean// forEach void// findAny Optional
// collect R eg:List
List
numbers = Arrays.asList(1, 2, 3, 4, 5);// 元素求和,有初始值的情况int sum = numbers.stream() .reduce(0, Integer::sum);System.out.println(sum);// 元素求和,无初始值Optional
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);// 内置countSystem.out.println(FakeDb.getMenu().stream().count());

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

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

归约应用举例

List
tList = FakeDb.getTransactions();// (1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。List
list01 = tList.stream() .filter(t -> t.getYear() == 2011) // 2011年的交易 .sorted(Comparator.comparing(Transaction::getValue)) // 交易额从低到高 .collect(Collectors.toList());System.out.println(list01);// (2) 交易员都在哪些不同的城市工作过?List
cityList = tList.stream() .map(t -> t.getTrader().getCity()) // 交易员所在城市 .distinct() // 去重 .collect(Collectors.toList());System.out.println(cityList);// (3) 查找所有来自于剑桥的交易员,并按姓名排序。List
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
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
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
optional7 = tList.stream() .map(Transaction::getValue) .reduce(Integer::max);System.out.println(optional7.get());// (8) 找到交易额最小的交易。Optional
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
.mapToInt(Dish::getCalories) // 返回 IntStream .sum(); // sum,max,min,average// sum,如果流是空的,sum默认返回0System.out.println(sum);// 转换回对象流FakeDb.getMenu().stream() .mapToInt(Dish::getCalories) .boxed(); // 转换为Stream
// 默认值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
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.generateStream.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);

其他,备注

遍历数组取索引值

遍历数组取索引值

// 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
// 方案一:IntStream.range(0, names.length) .forEach(i -> System.out.println(i + ", " + names[i]));// 方案二:AtomicInteger index = new AtomicInteger();List
list = Arrays.stream(names) .filter(n -> n.length() <= index.incrementAndGet()) .collect(Collectors.toList());// 方案三:使用GuavaStreams.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

转载于:https://my.oschina.net/yysue/blog/1928138

你可能感兴趣的文章
pxe和kickstart无人值守安装
查看>>
国外设计师眼中的原型工具Mockplus
查看>>
SBIT典型应用
查看>>
山寨山寨版手机安全卫士项目
查看>>
Could not find action or result
查看>>
我的友情链接
查看>>
sed删除基础使用
查看>>
RedHat5下scrumworks pro 5.0安装详细说明
查看>>
hibernate三级缓存概括
查看>>
如何在centos7上网卡名称回归传统
查看>>
智能工业监管控制系统 ——以遵化海祥机械项目为例
查看>>
linux/centos 解决Tomcat内存溢出,centostomcat
查看>>
Java练习:用IF()进行数字排序
查看>>
NFS
查看>>
求助不加域不能上网
查看>>
如何制定企业5s管理制度手册?
查看>>
maven添加本地jar包依赖
查看>>
jade分页备忘
查看>>
Java - PriorityQueue
查看>>
HTML5会成为移动互联网应用开发的未来吗?
查看>>