第36讲 Stream流

Stream流简述

Java8以后 API添加了一个新的抽象称为流Stream,Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

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

元素流在管道中经过中间方法(操作完后还可以继续其他操作)的处理,最后由最终方法(操作完后直接结束,不能调用其他方法)得到前面处理的结果。

作用: 结合Lambda表达式,简化集合、数组的相关操作。

Stream使用

1 生成Stream流

使用者方法说明来自类
单列集合default Stream stream()为当前单列集合创建Stream流Collection接口默认方法
双列集合无法直接获取,需要先转成单列集合
数组static Stream stream(T[] arr)为当前数组创建Stream流Arrays工具类
同类型零散数据static Stream of(T... values)为当前数据创建Stream流Stream接口

1.1 单列集合Stream流

stream()是Collection接口 默认方法 ,该方法不强制被重写。

ArrayList<String> list = new ArrayList<>();

Stream<String> stream = list.stream();

1.2 双列集合Stream流

创建的Stream流

HashMap<Integer, String> hm = new HashMap<>();

Set<Integer> keySet = hm.keySet();
Stream<Integer> keyStream = keySet.stream();

创建键值对的Stream流

HashMap<Integer, String> hm = new HashMap<>();

Set<Map.Entry<Integer, String>> entries = hm.entrySet();
Stream<Map.Entry<Integer, String>> entryStream = entries.stream();

1.3 数组Stream流

String[] arr = {"a", "b", "c"};

Stream<String> stream = Arrays.stream(arr);

1.4 同类型零散数据Stream流

int a = 1;
int b = 3;

Stream<Integer> stream = Stream.of(a, 2, b);

注意:

Stream接口中的of方法的形参是一个可变参数,可以传递零散的数据,也可以传递一个数组。但是数组必须是引用数据类型的,如果传递的是基本数据类型,它不会自动装箱,而是会把整个数组当成一个元素添加到Stream流中。

2 中间方法

方法说明
Stream filter(Predicate<? super T> p)筛选(过滤)
Stream limit(long maxSize)获取前maxSize个元素
Stream skip(long n)跳过前n个元素
Stream distinct()元素去重(依赖hashCode和equals方法)
static Stream<T> concat(Stream a, Stream b)合并a流和b流为一个流(来自Stream接口)
Stream map(Function<T, R> mapper)用于映射每个元素到对应的结果,可转换流中数据类型

注意:

  • 中间方法的返回值是新的Stream流,且每个Stream流只能使用一次,所以建议使用链式编程
  • 修改Stream流中的数据,不会影响原集合或原数组中的数据。

2.1 筛选(过滤)

方法filter(Predicate<? super T> p)

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class StreamDemo5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");

        //创建流并过滤
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //返回true :当前数据留下
                //返回false :当前数据舍弃
                return s.startsWith("张");
            }
        }).forEach(new Consumer<String>() {
        //遍历
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }
}

运行结果: 张三 \n 张五六 \n 张七八

Predicate接口和Consumer接口都是**函数式接口 **,可以使用Lambda表达式简写,如下。

import java.util.ArrayList;
import java.util.Collections;

public class StreamDemo5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");
        
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
    }
}

2.2 获取和跳过

方法:

limit(long maxSize) //获取前maxSize个元素

Stream skip(long n) //跳过前n个元素

案例: 获取“张五六”

import java.util.ArrayList;
import java.util.Collections;

public class StreamDemo6 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");
        
        //方法1 :先获取前3个:张三, 李四五, 张五六
        //       再去掉前2个:张五六
        list.stream().limit(3).skip(2).forEach(s -> System.out.println(s));
        
        //方法2 :先去掉前2个:张五六, 王五, 张七八, 李九
        //       再获取前1个:张五六
        list.stream().skip(2).limit(1).forEach(s -> System.out.println(s));
    }
}

2.3 去重

Stream distinct() //元素去重

该方法是利用HashSet去重的,依赖hashCode和equals方法。

所以如果是自定义数据类型,需要手动重写hashCode和equals方法。

import java.util.ArrayList;
import java.util.Collections;

public class StreamDemo7 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "张三", "张三", "李四五", "张五六", "王五", "张七八", "李九");

        System.out.println(list);	
        //输出结果: [张三, 张三, 张三, 李四五, 张五六, 王五, 张七八, 李九]
        list.stream().distinct().forEach(s -> System.out.print(s + ", "));	
        //输出结果: 张三, 李四五, 张五六, 王五, 张七八, 李九,
    }
}

2.4 合并流

static Stream<T> concat(Stream a, Stream b) //合并a流和b流为一个流

  • 该方法来自Stream接口

合并的两个流最好是同数据类型

如果不是同类型,合并后的流的数据类型就是两个流的父类,会导致无法使用本身特有方法(多态的弊端)。

import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Stream;

public class StreamDemo8 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "张三", "李四五");

        ArrayList<String> list2 = new ArrayList<>();
        Collections.addAll(list2, "张五六", "王五", "张七八", "李九");

        Stream.concat(list1.stream(), list2.stream())
                .forEach(s -> System.out.print(s + ", "));
        //输出结果: 张三, 李四五, 张五六, 王五, 张七八, 李九,
    }
}

2.5 转换类型

Stream map(Function<T, R> mapper) //转换流中数据类型

案例: 在字符串中截取年龄

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.Function;

public class StreamDemo9 {
    public static void main(String[] args) {
        //Stream<R> `map(Function<T, R> mapper)`  //转换流中数据类型
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三-19", "李四五-20", "张五六-18", "王五-22");

        /**
         * 参数1 :原流的数据类型
         * 参数2 :修改后的数据类型
         */
        list.stream().map(new Function<String, Integer>() {
                    /**
                     *该方法用于转换流中数据类型
                     * @param s 原流中的每一个元素
                     * @return 修改数据类型后的元素
                     */
                    @Override
                    public Integer apply(String s) {
                        //1.按"-"切割每一个字符串
                        String[] split = s.split("-");
                        //2.获取1号索引元素,即年龄。此时是String类型的。
                        String ageString = split[1];
                        //3.转换成Integer类型,并返回。
                        int ageInteger = Integer.parseInt(ageString);
                        return ageInteger;
                    }
                })
                //当map方法执行完毕后,流上的数据变成了Integer类型,所以forEach遍历时的参数是Integer类型。
                .forEach(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer ageInteger) {
                        System.out.print(ageInteger + ", ");
                        //输出结果: 19, 20, 18, 22, 
                    }
                });
    }
}

Consumer接口和Function接口是函数式接口 ,可以用Lambda简写:

import java.util.ArrayList;
import java.util.Collections;

public class StreamDemo9 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三-19", "李四五-20", "张五六-18", "王五-22");

        list.stream().map(s -> Integer.parseInt(s.split("-")[1]))
                .forEach(s -> System.out.print(s + ", "));
    }
}

3 最终方法

方法说明
void forEach(Consumer action)遍历流,以此获取流中元素
long count()统计流中数据个数
Object[] toArray()收集流中数据,放到Object类型数组中
A[] toArray(IntFunction<A[]> generator)收集流中数据,放到指定类型数组中
collect(Collector c)收集流中数据,放到集合中

3.1 统计

import java.util.ArrayList;
import java.util.Collections;

public class StreamDemo10 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");

        long count = list.stream().count();
        System.out.println(count);	// 6
    }
}

3.2 收集到数组中

空参方法 Object[] toArray() //收集流中数据,放到Object类型数组中

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class StreamDemo11 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");

        Object[] objectArr = list.stream().toArray();
        System.out.println(Arrays.toString(objectArr));	//[张三, 李四五, 张五六, 王五, 张七八, 李九]
    }
}

转成Object类型的数组往往不是我们需要的,所以可以采用带参方法:

A[] toArray(IntFunction<A[]> generator) //收集流中数据,放到指定类型数组中

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.IntFunction;

public class StreamDemo11 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");
        /**
         * toArray方法
         * @param new IntFunction...  仅负责创建一个指定类型的数组
         * @return  一个装有流中数据的、指定类型的数组
         * 底层 :会依次得到流里每一个数据,并把数据添加到数组中
         */
        //IntFunction的泛型:指定类型的数组的类型。
        String[] stringArr = list.stream().toArray(new IntFunction<String[]>() {
            /**
             * apply方法
             * @param value 流中数据的个数。要与数组的长度保持一致
             * @return 具体类型的数组
             * 方法体 :创建数组
             */
            @Override
            public String[] apply(int value) {
                return new String[value];
            }
        });
        System.out.println(Arrays.toString(stringArr));//[张三, 李四五, 张五六, 王五, 张七八, 李九]
    }
}

IntFunction接口是函数式接口,可以使用Lambda表达式简写,如下:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class StreamDemo11 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三", "李四五", "张五六", "王五", "张七八", "李九");
        
        String[] stringArr = list.stream().toArray(value -> new String[value]);
        System.out.println(Arrays.toString(stringArr));//[张三, 李四五, 张五六, 王五, 张七八, 李九]
    }
}

3.3 收集到List集合中

方法:

collect(Collector c) //收集流中数据,放到集合中

接口Collectors 中的方法 toList() //创建一个指定List集合

案例: 将所有的男性添加到List集合

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class StreamDemo12 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三-男-19", "李四五-女-18", "张五六-女-23", "王五-男-22", "张七八-女-20", "李九-男-22");


        List<String> collectList = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toList());
        System.out.println(collectList);	//[张三-男-19, 李九-男-22, 王五-男-22]
    }
}

3.4 收集到Set集合中

方法:

collect(Collector c) //收集流中数据,放到集合中

接口Collectors 中的方法 toSet() //创建一个指定Set集合

案例: 将所有的男性添加到Set集合

import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamDemo13 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三-男-19", "李四五-女-18", "张五六-女-23", "王五-男-22", "张七八-女-20", "李九-男-22");

        Set<String> collectSet = list.stream().filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toSet());
        System.out.println(collectSet);	 //[张三-男-19, 李九-男-22, 王五-男-22]
    }
}

3.5 收集到Map集合中

方法:

collect(Collector c) //收集流中数据,放到集合中

接口Collectors 中的方法

toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)//创建一个指定Map集合

Map集合的键唯一,所以不能在键的位置添加重复数据,否则会报错。

案例: 将所有的男性添加到Map集合

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class StreamDemo14 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三-男-19", "李四五-女-18", "张五六-女-23", "王五-男-22", "张七八-女-20", "李九-男-22");
        /**
         * 方法 toMap
         * 参数1 : 表示键的生成规则
         * 参数2 : 表示值的生成规则
         */
        Map<String, Integer> collectMap = list.stream()
                .collect(Collectors.toMap(
                        /**
                         * 参数1详解 :
                         *     Function的泛型1 : 表示流中每个数据的类型
                         *              泛型2 : 表示Map集合中“键”的数据类型
                         *     方法 apply
                         *     参数: 表示流中的每一个数据
                         *     方法体: 表示生成键的规则
                         *     返回值: 表示已经生成的键
                         */
                        new Function<String, String>() {
                            @Override
                            public String apply(String s) {
                                //姓名
                                return s.split("-")[0];
                            }
                        }
                        /**
                         * 参数2详解 :
                         *     Function的泛型1 : 表示流中每个数据的类型
                         *              泛型2 : 表示Map集合中“值”的数据类型
                         *     方法 apply
                         *     参数: 表示流中的每一个数据
                         *     方法体: 表示生成值的规则
                         *     返回值: 表示已经生成的值
                         */
                        , new Function<String, Integer>() {
                            @Override
                            public Integer apply(String s) {
                                //年龄
                                return Integer.parseInt(s.split("-")[2]);
                            }
                        }));
        System.out.println(collectMap);
        //{张三=19, 李九=22, 张七八=20, 张五六=23, 王五=22, 李四五=18}
    }
}

Function接口是函数式接口,可以使用Lambda表达式简写,如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

public class StreamDemo14 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三-男-19", "李四五-女-18", "张五六-女-23", "王五-男-22", "张七八-女-20", "李九-男-22");
        
        Map<String, Integer> collectMap = list.stream()
                .collect(Collectors.toMap(key -> key.split("-")[0], value -> Integer.parseInt(value.split("-")[2])));
        System.out.println(collectMap);
        //{张三=19, 李九=22, 张七八=20, 张五六=23, 王五=22, 李四五=18}
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值