Stream

Stream

原文(这篇文章写的真的太好了)
假如说们需要筛选出集合中的指定元素我们一般情况会这样做

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Test {
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1,2,3,4,5,6);
        System.out.println(selectNumber(integers));

    }

    private static List<Integer> selectNumber(List<Integer> integers) {
        List<Integer> temp = new ArrayList<>();
        for (Integer integer : integers) {
            if(integer%2 == 0){
                temp.add(integer);
            }
        }
        return temp;
    }
}

通过一个方法来实现也就是,但是如果说我希望筛选出其他的指定元素呢?是不是又得通过一个方法来实现,或者说我们可以通过一个一个的接口来实现,但是这样都很不方便。可以使用在Java中的策略模式。让编程变得更加简洁。like this

几个重要接口

1.Predicate 断言接口

一个参数返回一个blooean

(a)-> {return true|false;}

2.Function 函数接口

一个参数一个返回结果,类型可以不一样

(a) -> {return a操作后的结果}

3.BiPredicate 双参数断言

两个参数返回一个blooean

(a,b) -> {return true|false;}

4.BiFunction 双参数函数接口

两个参数返回ab操作后的结果

(a,b)-> {根据ab返回一个结果}

5.Consumer 消费接口

一个参数,没有结果

(a)-> {无返回}

6.BiConsumer 双参数消费者接口

两个参数,无返回

(a,b)->{无返回值}

7.Supplier 生产者接口

无参数,返回一个结果

() -> {return result;}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Test {
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1,2,3,4,5,6);
        System.out.println(selectNumber(integers, i ->  i % 2 == 0));

    }

    private static List<Integer> selectNumber(List<Integer> integers,Predicate<Integer> predicate) {
        List<Integer> temp = new ArrayList<>();
        for (Integer integer : integers) {
            if(predicate.test(integer)){
                temp.add(integer);
            }
        }
        return temp;
    }
}

但是我们今天用Stream来实现对集合的筛选

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TestStream {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        List<Integer> list1 = list.stream().filter(i -> i % 2 == 0).map(i -> i*2).collect(Collectors.toList());
        System.out.println(list1);
    }
}

什么是Stream?

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

流的使用

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。

流的操作类型分为两种:

1.Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

2.Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

还有一种操作被称为 short-circuiting。用以指:

1.对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。

2.对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。

当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件。

Stream获取

有多种方式生成 Stream Source:

从 Collection 和数组

1.Collection.stream()

2.Collection.parallelStream()

Arrays.stream(T array) or Stream.of()

1.从 BufferedReader
java.io.BufferedReader.lines()

静态工厂

1.java.util.stream.IntStream.range()

2.java.nio.file.Files.walk()

自己构建

1.java.util.Spliterator

其它

1.Random.ints()

2.BitSet.stream()

3.Pattern.splitAsStream(java.lang.CharSequence)

4.JarFile.stream()

Stream基本方法

stream():得到一个Stream

filter():过滤集合得到指定数据返回一个Stream

map():对每个元素进行修改操作,不会改变元素个数,也就是将一个元素映射成另一个元素

flatMap():把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的元素。

List<String> list2 = list.stream().flatMap(strings -> Arrays.stream(strings)).collect(Collectors.toList());

forEach():遍历集合,接收一个Consumer 消费者接口

list2.stream().forEach( (a) -> {
    System.out.println(a);
} );

count():统计流里有多少元素

System.out.println(list2.stream().count())

distint():去除重复元素

list2.stream().distint();

max():找集合的最大值返回一个Optional类型

    System.out.println(list2.stream().max())

min():找出集合的最小值返回一个Optional类型

    System.out.println(list2.stream().min())

sorted():对流中元素进行排序

List<Person> persons = new ArrayList();
 for (int i = 1; i <= 5; i++) {
 Person person = new Person(i, "name" + i);
 persons.add(person);
 }
List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
System.out.println(personList2);

groupingBy():对流中元素分组

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());

当然如果特殊一点你的流里全是数字的话,对整数的处理也有更加便利的方法

sum():求和

average():求平均值

Stream 的特性

1.不是数据结构

2.它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。

3.它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个

4.不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。

5.所有 Stream 的操作必须以 lambda 表达式为参数

6.不支持索引访问

7.你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。

8.很容易生成数组或者 List

9.惰性化

10.很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。

11.Intermediate 操作永远是惰性化的。

12.并行能力

13.当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。

14.可以是无限的
集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值