JAVA8实战-学习笔记2-数据处理好工具Stream

学习笔记2-数据处理好工具Stream(一)


参考文献:
JAVA8实战
Java 8 中的 Streams API 详解
Java8初体验(二)Stream语法详解

为什么需要流?

Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

什么是流?

  1. Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
  2. Stream就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
  3. 而和迭代器又不同的是,Stream可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

关于流的操作

  • 常见的操作如下
    map(映射)/flatMap(扁平化映射):它的作用就是把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素.用途可以是从大集合里提取小集合可以适当处理小集合。另外flatMap会把子Stream中的元素压缩到父集合中。
    //转换大写
    List output = wordList.stream().
    map(String::toUpperCase).
    collect(Collectors.toList());
    //平方数
    List nums = Arrays.asList(1, 2, 3, 4);
    List squareNums = nums.stream().
    map(n -> n * n).
    collect(Collectors.toList());
    //一对多
    Stream> inputStream = Stream.of(
     Arrays.asList(1),
     Arrays.asList(2, 3),
     Arrays.asList(4, 5, 6));
    Stream outputStream = inputStream.
    flatMap((childList) -> childList.stream());

map示意图

JAVA8实战-学习笔记2-数据处理好工具Stream_第1张图片

flatmap示意图

JAVA8实战-学习笔记2-数据处理好工具Stream_第2张图片
filter: filter 对原始 Stream 进行某项筛选,通过筛选的元素被留下来生成一个新 Stream。

//留下偶数
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =Stream.of(sixNums).filter(n -> n%2 == 0)
                .toArray(Integer[]::new);

forEach:

//打印出所有男性员工的姓名
roster.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.forEach(p -> System.out.println(p.getName()));

reduce(归纳/汇聚): 这个方法的主要作用是把 Stream元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接数值的 sum、min、max、average 都是特殊的 reduce。

// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); 
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); 
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").
 filter(x -> x.compareTo("Z") > 0).
 reduce("", String::concat);

sorted(排序):

//2012年所发生的交易额排序
List transactions = (List) lists.stream()
                .filter(transaction -> transaction.getYear() == 2012)
                .sorted(Comparator.comparing(Transaction::getValue)).collect(Collectors.toList());

结束语

  • Stream不是数据结构
  • 它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
  • 它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
  • 所有 Stream 的操作必须以 lambda 表达式为参数
  • 不支持索引访问
  • 你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。
  • 很容易生成数组或者 List
  • 惰性化
  • 很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
  • Intermediate 操作永远是惰性化的。
  • 并行能力
  • 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
  • 可以是无限的
  • 集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting(短路) 操作可以对无限的 Stream 进行运算并很快完成。

参考文献:
JAVA8实战
Java 8 中的 Streams API 详解
Java8初体验(二)Stream语法详解

你可能感兴趣的:(java,JAVA学习)