Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();

关于中间函数的详解,下文讲

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • <strong元素队列< strong="">元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

生成流的方式

在 Java 8 中, 集合接口有2种方法来生成流:

  • stream() − 为集合创建串行流。

  • parallelStream() − 为集合创建并行流。

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
Stream<Integer> stream3 = Stream.of(1, 2, 3);

其他的几种创建流的方式

  • Stream的静态方法of:
/**
* 创建流
*/
@Test
public void test01(){
//Stream 静态方法
//Stream.of(...)
Stream<Integer> stream3 = Stream.of(1, 2, 3);
}
  • Stream的静态方法iterate
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x+2);
stream4.limit(10).forEach(System.out::println);

其中iterate方法解释如下:

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
返回有序无限连续 Stream由函数的迭代应用产生 f至初始元素 seed ,产生 Stream包括 seed , f(seed) , f(f(seed)) ,等

UnaryOperator是一个函数式接口:

  • Stream的静态方法generate
    //生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);

其中generate方法解释如下:

static <T> Stream<T> generate(Supplier<T> s)
返回无限顺序无序流,其中每个元素由提供的 Supplier 。

传递一个函数式接口,lamabda表达式作为参数。

其中forEach方法解释如下:

void forEach(Consumer<? super T> action)
对此流的每个元素执行操作。

传递一个函数式接口,lamabda表达式作为参数

Stream的中间操作函数

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!

而在终止操作时一次性全部处理,称为”惰性求值“

筛选与切片

  • filter:接收 Lambda ,从流中排除某些元素
  • limit:截断流,使其元素不超过给定数量
  • skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
  • distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
); @Test
public void test01(){
emps.stream()
.filter((x) -> x.getAge() > 35)
.limit(3) //短路?达到满足不再内部迭代
.distinct()
.skip(1)
.forEach(System.out::println); }

映射map

  • map:接收函数式接口,将元素转换为其他形式(简单的替换)或提取信息(传入其他对象当中存在的方法);接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
<R> Stream<R> map(Function<? super T,? extends R> mapper)
返回由给定函数应用于此流的元素的结果组成的流。
  • flatMap:接收一个函数式接口。优化遍历情形:将流中的对象都换成流(即流的嵌套),然后把所有流重新连接成一个流,返回的对象不再是嵌套流,而是流对象。便于遍历
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
返回由通过将提供的映射函数应用于每个元素而产生的映射流的内容来替换该流的每个元素的结果的流。

map示例:接收一个lamabda表达式

@Test
public void test02(){
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
}

其中forEach(System.out::println)是lamabda表达式的简化写法,等效于

forEach(x -> {
System.out.println(x);
});

map存在的问题:假设现在存在一个自定义的流生成函数filterCharacter

class TestStreamApi2{
public Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
} return list.stream();
}
@Test
public void test(){
List<String> list = Arrays.asList("a", "b", "c");
    TestStreamApi2 test02 = new TestStreamApi2(); 
    Stream<Stream<Character>> stream= list.stream().Map(test02::filterCharacter); 

//遍历嵌套流
stream.forEach((sm)-> sm.forEach(System.out::println) );
}
}

其中Map(test02::filterCharacter)是lamabda表达式的简化写法,等效于

Map(str->{
    test02.filterCharacter(str)}
);

我们知道流处理中间操作毋庸置疑的是返回流,而自定义的filterCharacter(String str)仍然返回流,即流中嵌套流,返回最终返回的是Stream<Stream<Character>>

Stream<Stream<Character>>=
list.stream().flatMap(test02::filterCharacter)

这样的遍历多麻烦啊!!

flatMap示例:接收一个函数


public Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
} return list.stream();
} @Test
public void test03(){
List<String> list = Arrays.asList("a", "b", "c");
Test02 test02 = new Test02();
list.stream()
.flatMap(testStreamApi2::filterCharacter)
.forEach(System.out::println);
}

排序

  • sorted():自然排序
Stream<T> sorted()
返回由此流的元素组成的流,根据自然顺序排序。
  • sorted(Comparator c):定制排序
Stream<T> sorted(Comparator<? super T> comparator)
返回由该流的元素组成的流,根据提供的 Comparator进行排序。

Comparable:自然排序

@Test
public void test04(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream()
.sorted() //comparaTo()
.forEach(System.out::println);
}

Comparator:定制排序

@Test
public void test05(){
emps.stream()
.sorted((e1, e2) -> { //compara()
if (e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
} else {
return e1.getAge().compareTo(e2.getAge());
}
})
.forEach(System.out::println);
}

查找 / 匹配

终止操作:

  • allMatch:检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配所有元素
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值
public enum Status {
FREE, BUSY, VOCATION;
} @Test
public void test01(){
List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION);
//s代指list中的遍历元素
boolean flag1 = list.stream()
.allMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag1); boolean flag2 = list.stream()
.anyMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag2); boolean flag3 = list.stream()
.noneMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag3); // 避免空指针异常
Optional<Status> op1 = list.stream()
.findFirst();
// 如果Optional为空 找一个替代的对象
Status s1 = op1.orElse(Status.BUSY);
System.out.println(s1); Optional<Status> op2 = list.stream()
.findAny();
System.out.println(op2); long count = list.stream()
.count();
System.out.println(count);
}

归约 / 收集

  • 归约:reduce可以将流中的数据反复结合起来,得到一个值
Optional<T> reduce(BinaryOperator<T> accumulator)
使用 associative累积函数对此流的元素执行 reduction ,并返回描述减小值的 Optional (如果有)。
T reduce(T identity, BinaryOperator<T> accumulator)
使用提供的身份值和 associative累积功能对此流的元素执行 reduction ,并返回减小的值。
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
执行 reduction在此流中的元素,使用所提供的身份,积累和组合功能。
  • 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
<R,A> R collect(Collector<? super T,A,R> collector)
使用 Collector对此流的元素执行 mutable reduction Collector 。
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
对此流的元素执行 mutable reduction操作。

reduce:求和案例

/**
* Java:
* - reduce:需提供默认值(初始值)
* Kotlin:
* - fold:不需要默认值(初始值)
* - reduce:需提供默认值(初始值)
*/
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer sum= list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
}

Collect收集案例:collect(Collector c)其中Collector接口方法中的实现决定了如何对流执行收集操作(如收集到List,Set,Map)。

<R,A> R collect(Collector<? super T,A,R> collector)
使用 Collector对此流的元素执行 mutable reduction Collector 。
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
对此流的元素执行 mutable reduction操作。

正好Collectors收集器工具类提供了很多静态方法,可以方便的创建收集器实例

List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
); @Test
public void test02(){
//放入List
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println); //放入Set
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println); //放入LinkedHashSet
LinkedHashSet<String> linkedHashSet = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedHashSet::new));
linkedHashSet.forEach(System.out::println);
} @Test
public void test03(){
//总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count); //平均值
//Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时(或不存在时),可以使用 ClassName :: Method
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg); //总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum); //最大值
Optional<Employee> max = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get()); //最小值
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
} @Test
public void test04(){
//分组
Map<Integer, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getId));
System.out.println(map); //多级分组
Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
if (e.getAge() > 35) {
return "开除";
} else {
return "继续加班";
}
})));
System.out.println(mapMap); //分区
Map<Boolean, List<Employee>> listMap = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321));
System.out.println(listMap);
} @Test
public void test05(){
//总结
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getMin());
System.out.println(dss.getSum());
System.out.println(dss.getCount());
System.out.println(dss.getAverage()); //连接
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("-")); //可传入分隔符
System.out.println(str);
}

其中:Collectors.toCollection(LinkedHashSet::new)

static <T,C extends Collection<T>>
Collector<T,?,C> toCollection(Supplier<C> collectionFactory)
返回一个 Collector ,按照遇到的顺序将输入元素累加到一个新的 Collection中。

并行流

  • 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
  • Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
@Test
public void test03(){
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
LongStream.rangeClosed(0, 100000000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum); }

Fork / Join 框架:

Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork) 成若干个小任务(拆到给出的临界值为止),再将一个个的小任务运算的结果 进行join汇总

Fork / Join 框架与传统线程池的区别:

1.采用 “工作窃取” 模式 (work-stealing)

当执行新的任务时它可以将其拆分成 更小的任务执行,并将小任务加到线程队列中,当没有任务执行时,再从一个随机线程的队列中偷一个并把它放在自己的队列中

2.相对于一般的线程池实现 ,fork/join 框架的优势体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行那么该线程会处于等待状态。

而在fork/join 框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题(窃取过来)来执行,这种方式减少了线程的等待时间,提高了性能

Fork / Join 实现:

public class ForkJoinCalculate extends RecursiveTask<Long> {

    private static final long serialVersionUID = 1234567890L;

    private long start;
private long end; private static final long THRESHPLD = 10000; public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
} @Override
protected Long compute() {
long length = end - start; if (length <= THRESHPLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
} else {
long middle = (start + end) / 2; ForkJoinCalculate left = new ForkJoinCalculate(start, end);
left.fork(); //拆分子任务 压入线程队列 ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
right.fork(); return left.join() + right.join();
} return null;
}
} public class TestForkJoin { /**
* ForkJoin 框架
*/
@Test
public void test01(){
Instant start = Instant.now(); ForkJoinPool pool = new ForkJoinPool();
ForkJoinCalculate task = new ForkJoinCalculate(0, 100000000L); Long sum = pool.invoke(task);
System.out.println(sum); Instant end = Instant.now();
System.out.println(Duration.between(start, end).getNano());
} /**
* 普通 for循环
*/
@Test
public void test02(){
Instant start = Instant.now(); Long sum = 0L;
for (long i = 0; i < 100000000L; i++) {
sum += i;
} Instant end = Instant.now();
System.out.println(Duration.between(start, end).getNano());
}
}

Java新特性-stream流的更多相关文章

  1. java新特性stream

    java新特性stream,也称为流式编程. 在学习stream之前先了解一下java内置的四大函数 第一种函数式函数,后面是lambda表达式写法 /*Function<String,Inte ...

  2. 这可能是史上最好的 Java8 新特性 Stream 流教程

    本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...

  3. 再来看看Java的新特性——Stream流

    半年前开始试着使用Java的新特性,给我印象最深的就是Stream流和Optional.其中Stream提高了看法效率,让代码看起来十分清爽. 为什么要使用流? 摘要中已经说明了,为了提高开发效率.流 ...

  4. Java1.8新特性 - Stream流式算法

    一. 流式处理简介   在我接触到java8流式数据处理的时候,我的第一感觉是流式处理让集合操作变得简洁了许多,通常我们需要多行代码才能完成的操作,借助于流式处理可以在一行中实现.比如我们希望对一个包 ...

  5. Java8新特性 Stream流式思想(一)

    遍历及过滤集合中的元素使用传统方式遍历及过滤集合中的元素package cn.com.zq.demo01.Stream.test01.Stream; import java.util.ArrayLis ...

  6. Java8新特性 Stream流式思想(二)

    如何获取Stream流刚开始写博客,有一些不到位的地方,还请各位论坛大佬见谅,谢谢! package cn.com.zq.demo01.Stream.test01.Stream; import org ...

  7. java8 新特性Stream流的应用

    作为一个合格的程序员,如何让代码更简洁明了,提升编码速度尼. 今天跟着我一起来学习下java 8  stream 流的应用吧. 废话不多说,直入正题. 考虑以下业务场景,有四个人员信息,我们需要根据性 ...

  8. Java8新特性 Stream流式思想(三)

    Stream接口中的常用方法 forEach()方法package cn.com.cqucc.demo02.StreamMethods.Test02.StreamMethods; import jav ...

  9. JDK8新特性---stream流

    项目上用到了stream流,找篇blog,转载一下,介绍下Stream流的用法. 1 流概述  流是 JDK8 新增的成员,允许以声明性方式处理数据集合,可以把 Stream 流看作是遍历数据集合的一 ...

  10. Java8新特性Stream流应用示例

    Java8新特性介绍 过滤集合 List<String> newList = list.stream().filter(item -> item != null).collect(C ...

随机推荐

  1. 【转载】【转载视频】 如何成为好的IT技术面试官

    在外网找到了一个IT技术面试官的面试心得,感觉还不错,挺有借鉴的,这里mark一下. 地址: https://www.youtube.com/watch?v=yFMmkoqDPlM ========= ...

  2. java中sleep与 yield 区别

    1.背景 在多线程的使用中你会看到这个两个方法sleep()与yield()这两方法有什么作用呢? 请看下面案例演示 2.测试 package com.ldp.demo01; import com.c ...

  3. Java基础之数值类型之间的转换

    经常需要将一种数值类型转换为另一种数值类型.下图 给出了数值类型之间的合法 转换. 在图中有 6 个实心箭头,表示无信息丢失的转换:有 3 个虚箭头, 表示可能有精度 损失的转换. 例如,123 45 ...

  4. AvaloniaChat—从源码构建指南

    AvaloniaChat介绍 一个使用大型语言模型进行翻译的简单应用. 我自己的主要使用场景 在看英文文献的过程中,比较喜欢对照着翻译看,因此希望一边是英文一边是中文,虽然某些软件已经自带了翻译功能, ...

  5. 初三年后集训测试---T1排序

    初三年后集训测试 $T 1 $ 排序 $$HZOI$$ ·题意: 给定 \(4n\) 个整数,求 : \[\max\{\sum_{i=1}^{4n}(A_{i,1} \times A_{i,2} - ...

  6. 生产者消费者模式,以及基于BlockingQueue的快速实现

    生产者消费者模式,以及基于BlockingQueue的快速实现什么是生产者消费者模式,简单来说就是有两个角色,一个角色主要负责生产数据,一个角色主要负责消费(使用)数据.那么生产者直接依赖消费者,然后 ...

  7. ServletConfig 类和ServletContext 类

    ServletConfig 类 ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类. Servlet 程序和 ServletConfig 对象都是由 Tomcat ...

  8. 【YashanDB知识库】YAC修改参数后关闭数据库夯住

    [问题分类]功能使用 [关键字]YAC,参数,SHM_POOL_SIZE,重启 [问题描述]YashanDB共享集群修改数据库配置参数,重启数据库时,数据库无法关闭. [问题原因分析]YAC的SHM_ ...

  9. C++ : 仅添加一个引用& 就直接导致程序崩溃

    问题描述 在项目某次开发中,测试过程中出现了coredump问题.经过asan工具检测,报了heap-use-after-free内存错误,最终定位到竟是无意中添加了一个引用&导致的! 开发时 ...

  10. 工具 – Cypress

    介绍 Cypress 是一款 e2e 测试工具.每当我们写好一个组件或者一个页面之后,我们会想对整体做一个测试. 在不使用工具的情况下,我们会开启 browser,然后做一系列点击.滚动.填 form ...