Java Stream流

Stream介绍

        Stream(流)是一个来自数据源的元素队列,元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。数据源:流的来源,可以是集合,数组 等。

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

Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluentstyle)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。

内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。

 

        这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。而最右侧的数字3是最终结果。这里的 filter 、 map 、 skip 都是在对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法 count执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。

备注:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。

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

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

Stream操作

1、获取流:

        java.util.stream.Stream<T> 是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)获取一个流非常简单,有以下几种常用的方式:所有的 Collection 集合都可以通过 stream 默认方法获取流;Stream 接口的静态方法 of 可以获取数组对应的流。

1.1、根据Collection获取流

        java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。

List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();

1.2、根据Map获取流

        java.util.Map 接口不是 Collection 的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况:

Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();

1.3、据数组获取流

        如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法of 。

String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
Stream<String> stream = Stream.of(array);

2、Stream中的常用方法

2.1、forEach:逐一处理

void forEach(Consumer<? super T> action);

        该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。

private static void forEachDemo(){
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
        stream.forEach(s -> System.out.println(s));
    }

输出:

forEach ==================
张无忌
张翠山
张三丰
张一元

2.2、filter:过滤

Stream<T> filter(Predicate<? super T> predicate);

        该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。

private static void filterDemo(){
        String[] array = { "张无忌", "张三丰", "周芷若" };
        Stream<String> stream = Stream.of(array);
        Stream<String> result = stream.filter(s -> s.startsWith("张"));
        result.forEach(s -> System.out.println(s));
    }

        输出:

filter ==================
张无忌
张三丰

2.3、map: 映射

        如果需要将流中的元素映射到另一个流中,可以使用 map 方法。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

        该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

private static void mapDemo(){
        String[] array = {"10", "12", "18"};
        Stream<String> stringStream = Stream.of(array);
        Stream<Integer> integerStream = stringStream.map(s -> Integer.parseInt(s));
        integerStream.forEach(integer -> System.out.println(integer));
    }

        输出:

map ==================
10
12
18

2.4、count:统计流中的元素个数

        正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:

long count();

        该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。

private static void countDemo(){
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
        long num = stream.count();
        System.out.println(num);
    }

        输出:

count ==================
4

2.5、limit:取用前几个

        limit 方法可以对流进行截取,只取用前n个。

Stream<T> limit(long maxSize);

        参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。

private static void limitDemo(){
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
        Stream<String> newStream = stream.limit(3);
        newStream.forEach(s -> System.out.println(s));
    }

        输出:

limit ==================
张无忌
张翠山
张三丰

2.6、skip:跳过前几个

        如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:

Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
n必须大于等于0,如果大于当前流中的元素个数,则产生一个空流。
private static void skipDemo(){
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
        Stream<String> newStream = stream.skip(2);
        newStream.forEach(s -> System.out.println(s));
    }

        输出:

skip ==================
张三丰
张一元

2.7、concat:组合

        如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

        例子:

private static void concatDemo(){
        Stream<String> stream1 = Stream.of("张三");
        Stream<String> stream2 = Stream.of("李四");
        Stream<String> stream = Stream.concat(stream1,stream2);
        stream.forEach(s -> System.out.println(s));
    }

        输出:

concat ==================
张三
李四

2.8、sorted:排序

Stream<T> sorted()

        sorted 方法用于对流进行排序。

private static void sortedDemo(){
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
        stream.limit(3).sorted().forEach(System.out::println);
    }

        输出:

sorted ==================
张三丰
张无忌
张翠山

2.9、元素收集

        1)收集到List集合:

流对象.collect( Collectors.toList() )

获得List集合。

        2)收集到Set集合:

流对象.collect( Collectors.toSet() )

获得Set集合。

        3)收集到数组:

流对象. toArray()

由于泛型擦除的原因,返回值类型是Object[]

2.10、分组

        groupingBy()方法是Collectors类中的静态方法

public static <T, K, A, D>
    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream)

        例子:

private static void groupingByDemo(){
        List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya");
        Map<String,Long> result = items.stream()
                .collect(Collectors.groupingBy(
                        Function.identity(),Collectors.counting()
                    )
                );
        result.entrySet().stream().forEach(System.out::println);
    }

        输出:

groupingBy ==================
papaya=1
orange=1
banana=2
apple=3

2.11、分区

public static <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)

        partitioningBy() 分区方法,可以看做是分组的一种特殊情况,在分区中key只有两种情况:true或false,目的是将待分区集合按照条件一分为二。

// 按是否以"a'开头分为两部分
private static void partitioningByDemo(){
        List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya");
        Map<Boolean, List<String>> result = items.stream().collect(Collectors.partitioningBy(s -> s.startsWith("a")));
        System.out.println(result);
    }

        输出:

partitioningBy ==================
{false=[banana, orange, banana, papaya], true=[apple, apple, apple]}

2.12、flagMap

        一对多转换,flatMap与map的区别在于 flatMap是将一个流中的每个值都转成一个个流,然后再将这些流扁平化成为一个流。

/*使用map并不能实现我们现在想要的结果,而flatMap是可以的。
    这是因为在执行map操作以后,我们得到是一个包含多个字符串(构成一个字符串的字符数组)的流,
    此时执行distinct操作是基于在这些字符串数组之间的对比,所以达不到我们希望的目的;
    flatMap将由map映射得到的Stream<String[]>,转换成由各个字符串数组映射成的流Stream<String>,
    再将这些小的流扁平化成为一个由所有字符串构成的大流Steam<String>,从而能够达到我们的目的。
     */
    private static void flagMapDemo(){
        // 我们希望输出构成这一数组的所有非重复字符
        String[] strings = {"Hello", "World"};
        List l1 = Arrays.stream(strings).map(str -> str.split("")).map(str2->Arrays.stream(str2)).distinct().collect(Collectors.toList());
        List l2 = Arrays.asList(strings).stream().map(s -> s.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
        System.out.println(l1.toString());
        System.out.println(l2.toString());
    }

        输出:

flagMap ==================
[java.util.stream.ReferencePipeline$Head@15975490, java.util.stream.ReferencePipeline$Head@6b143ee9]
[H, e, l, o, W, r, d]

        和map的区别:

    public static void main(String[] args) {

        List<String> cityListOne = new ArrayList<>();
        cityListOne.add("郑州");
        cityListOne.add("濮阳");
        List<String> cityListTwo = new ArrayList<>();
        cityListTwo.add("廊坊");
        cityListTwo.add("邢台");
        List<String> cityListThree = new ArrayList<>();
        cityListThree.add("大同");
        cityListThree.add("太原");
        List<String> cityListFour = new ArrayList<>();
        cityListFour.add("南昌");
        cityListFour.add("九江");

        Address addressOne = new Address();
        addressOne.setProvince("河南");
        addressOne.setCityList(cityListOne);

        Address addressTwo = new Address();
        addressTwo.setProvince("河北");
        addressTwo.setCityList(cityListTwo);

        Address addressThree = new Address();
        addressThree.setProvince("山西");
        addressThree.setCityList(cityListThree);

        Address addressFour = new Address();
        addressFour.setProvince("江西");
        addressFour.setCityList(cityListFour);

        List<Address> addresseList = new ArrayList<>();
        addresseList.add(addressOne);
        addresseList.add(addressTwo);
        addresseList.add(addressThree);
        addresseList.add(addressFour);

        //使用map输出所有的城市名称
        addresseList.stream()
                .map(a -> a.getCityList())
                .forEach(cityList->{ cityList.forEach(city -> System.out.print(city));
        });
        System.out.println("");
        //使用flatMap输出所有城市名称
        addresseList.stream()
                .flatMap(a -> a.getCityList().stream())
                .forEach(city -> System.out.print(city));


    }

        map和flagMap结果一样,但map处理方式是在foreach中将cityList作为集合再次循环,而flatMap可以直接将cityList中的城市直接进行循环输出。也就是说flatMap可以获取集合中的集合的流在外层可以直接处理。

2.13、查找

allMatch(Predicate<? super T> predicate)

        用于检测是否全部都满足指定的参数行为,如果全部满足则返回true

anyMatch(Predicate<? super T> predicate)

        anyMatch则是检测是否存在一个或多个满足指定的参数行为,如果满足则返回true

noneMatch(Predicate<? super T> predicate)

        oneMatch用于检测是否不存在满足指定行为的元素,如果不存在则返回true

findFirst()

        findFirst用于返回满足条件的第一个元素

findAny()

        findAny相对于findFirst的区别在于,findAny不一定返回第一个,而是返回任意一个

private static void findDemo(){
        List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya");
        Stream<String> stream = items.stream();
        System.out.println(stream.allMatch(s -> s.contains("a")));
        Stream<String> stream1 = items.stream();
        System.out.println(stream1.filter(s -> s.contains("a")).findFirst().get());
        Stream<String> stream2 = items.stream();
        System.out.println(stream2.filter(s -> s.contains("a")).findAny().get());
    }

        输出:

findMap ==================
true
apple
apple

2.14、字符串拼接

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)

        参数为连接符,返回值为一个集合。

private static void joinDemo(){
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        System.out.println(list);
        System.out.println(list.stream().collect(Collectors.joining(",")));
    }

        输出:

join ==================
[张无忌, 周芷若, 赵敏, 张强, 张三丰]
张无忌,周芷若,赵敏,张强,张三丰

2.15、归约

Optional<T> reduce(BinaryOperator<T> accumulator);

        rudece方法的功能是从一个流中生成一个值,reduce方法参数为一个函数,返回值为Optional对象。

private static void reduceDemo(){
        List<Integer> num = Arrays.asList(1, 2, 4, 5, 6, 7);
        // 求和
        num.stream().reduce((x, y) -> x+y ).ifPresent(System.out::println);
        // 求最大值
        num.stream().reduce(Integer::max).ifPresent(System.out::println);
    }

        输出:

reduce ==================
25
7

2.16、去重

Stream<T> distinct();

        例子;

private static void distinctDemo(){
        List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya");
        Stream stream = items.stream().distinct();
        stream.forEach(System.out::println);
    }

        输出:

distinct ==================
apple
banana
orange
papaya
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值