likes
comments
collection
share

Java中的Stream流到底是啥?怎么用?

作者站长头像
站长
· 阅读数 14

最近编码的时候用到了Stream这个东西,以前也用过,但是对它没有一个系统的认知。在好奇心的驱动下还是决定花一些时间去系统地学一学。下面是我看了B站视频(黑马)后的总结,不了解Stream的掘友可以看看本文,节省大家再去看视频的时间。

一、创建不可变的集合

  不可变的集合,顾名思义,就是不想让别人修改集合中的内容,也无法修改。如何创建呢?

(一)创建格式

  在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合:

  • static List of(E...elements) 创建一个具有指定元素的List集合对象。
  • static Set of(E...elements) 创建一个具有指定元素的Set集合对象。
  • static <K , V> Map<K,V> of(E...elements) 创建一个具有指定元素的Map集合对象。

(List.of在jdk8中没有,需要jdk9及以上的版本。)

public static void main(String[] args) {
    List<String> list = List.of("张三","李四","王五");
    for(String tmp : list){
        System.out.println(tmp);
    }
    System.out.println("----------------------------");
    Set<Integer> set = Set.of(1,2,3,4);
    for(int x : set){
        System.out.println(x);
    }
    System.out.println("-----------------------------");
    Map<String, Integer> map = Map.of("王五", 1, "李四", 2);
    Set<Map.Entry<String, Integer>> entries = map.entrySet();
    for(Map.Entry<String, Integer> entry : entries){
        System.out.println(entry.getKey() + ":" + entry.getValue());
    }
    System.out.println("------------------------------");
}

Java中的Stream流到底是啥?怎么用?

  • 这些集合不能添加,不能删除,不能修改,如果修改会抛异常:
    public static void main(String[] args) {
        List<String> list = List.of("张三","李四","王五");
        list.remove(0);
    }

结果:

Java中的Stream流到底是啥?怎么用?

  • 这里的Set集合中不能有相同的值,Map不能有相同的key,否则抛异常。
public static void main(String[] args) {
    Set<Integer> set = Set.of(1,1,2,3,4);
}

Java中的Stream流到底是啥?怎么用?

  • Map.of()里的参数最多传 10 个键值对,源码里of()方法参数最多的也就10对。

Java中的Stream流到底是啥?怎么用?   那为什么不用可变参数呢?因为可变参数只能有一个,并且要在参数列表的末尾。如果想要存超过10个的键值对,我们可以利用数组->可变参数

    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"a");
        map.put(2,"b");
        map.put(3,"c");
        map.put(4,"d");
        map.put(5,"e");
        map.put(6,"f");
        map.put(7,"g");
        map.put(8,"h");
        map.put(9,"i");
        map.put(10,"j");
        map.put(11,"k");
        map.put(12,"l");
        //获取所有的键值对
        Set<Map.Entry<Integer,String>> entries = map.entrySet();
        //把entries变成一个数组
        //如果集合的长度 〉数组的长度﹔数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
        //如果集合的长度〈=数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用原来的数组
        Map.Entry[] array = entries.toArray(new Map.Entry[0]);
        //生成不可变map集合,ofEntries()的参数是可变参数,可变参数可以使用数组。
        Map map1 = Map.ofEntries(array);
    }

第二种方式,但是要jdk10及以上:

    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"a");
        map.put(2,"b");
        map.put(3,"c");
        map.put(4,"d");
        map.put(5,"e");
        map.put(6,"f");
        map.put(7,"g");
        map.put(8,"h");
        map.put(9,"i");
        map.put(10,"j");
        map.put(11,"k");
        map.put(12,"l");
        //生成不可变集合
        Map<Integer,String> map1 = Map.copyOf(map);
    }

(二)不可变集合的作用

  • 保证线程安全:不可变集合是一种在创建之后就不再变更的对象,这种特性使得它们天生支持线程安全。
  • 防止数据被篡改:不可变集合可以用于封装一些敏感或重要的数据,防止它们被外部程序或不可信的库修改或破坏。

二、Stream 流

(一)Stream 的思想

  Stream 为什么叫流?可以通过下面的案例来引入这个概念。

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅");
        list.add("陈小帅");
        list.add("小红");
        list.add("李四");
        list.add("陈四");
        //筛选姓陈的,并且名字是3个字的人
        list.stream().filter(name->name.startsWith("陈"))
                     .filter(name->name.length()==3)
                     .forEach(name-> System.out.println(name));
    }

  Stream 就像流水线一样,这个流水线第一道工序就是过滤掉不姓陈的,第二道工序过滤掉字的数量不等于3的,最后的工序就是打印。

(二)Stream流的使用步骤

  1. 得到一条Stream流水线,把数据放上去。
  2. 利用Stream流中的API进行各种操作。
    1. 中间方法:方法调用后还可以调用其它方法,如上面的filter。
    2. 终结方法:最后一步,调用后不能调用其它方法。

1.把数据放到流水线上

获取方式方法名说明
单列集合default Stream stream()collection中的默认方法(由于单列集合实现了Collection接口,可以直接.stream())
双列集合(Map等)无法直接使用stream流
数组public static Stream stream(T[] array)Arrays工具类中的静态方法
零碎数据public static Stream stream(T[] array)Stream接口中的静态方法
    public static void main(String[] args) {
        //1.单列集合
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"a","b","b","b","c","d","e");//添加数据
        list.stream().forEach(s -> System.out.println(s));//流水线操作

        //2.双列集合
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"a");
        map.put(2,"b");
        map.put(3,"c");
        map.put(4,"d");
        map.put(5,"e");
        map.put(6,"f");
        map.keySet().stream().forEach(s-> System.out.println(s));//方式1:获取key的流水线,利用key与value的关系来操作整体
        map.entrySet().stream().forEach(entry-> System.out.println(entry));//方式2:获取键值对的流水线

        //3.数组
        int[] arr = {1,2,3,4,5,6,7,8};
        Arrays.stream(arr).forEach(x-> System.out.println(x));//利用 Arrays 工具类来获取流
        String[] strs = {"a","b","c"};
        Arrays.stream(strs).forEach(str-> System.out.println(str));
        
        //4.零散的数据
        Stream.of("a","b","c","d","e").forEach(x-> System.out.println(x));//利用 Stream 来获取
    }

2.中间方法

Stream方法中文解释作用
Stream filter(Predicate<? super T> predicate)筛选过滤出符合条件的元素
Stream limit(long maxSize)限制截取前maxSize个元素
Stream skip(long n)跳过跳过前n个元素
Stream distinct()去重去除重复的元素(根据hashCode和equals方法)
static Stream concat(Stream a, Stream b)合并将两个Stream合并为一个
Stream map(Function<T, R> mapper)映射将每个元素转换为另一种类型或形式

注意1:每个中间方法都是返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程。

注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据。

  • filter
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅");
        list.add("陈小帅");
        list.add("小红");
        list.add("李四");
        list.add("陈四");
        //筛选姓陈的,并且名字是3个字的人
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //如果返回值为true,表示当前数据留下
                //如果返回值为false,表示当前数据舍弃不要
                return s.startsWith("陈");
            }
        }).forEach(s -> System.out.println(s));//通过匿名内部类
        
        
        list.stream().filter(s->s.startsWith("陈")).forEach(s -> System.out.println(s));//通过 lambda
    }

Java中的Stream流到底是啥?怎么用?

  • limit
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅");
        list.add("陈小帅");
        list.add("小红");
        list.add("李四");
        list.add("陈四");
        //截取前 3 个数据
        list.stream().limit(3).forEach(s -> System.out.println(s));
    }

Java中的Stream流到底是啥?怎么用?

  • skip
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅");
        list.add("陈小帅");
        list.add("小红");
        list.add("李四");
        list.add("陈四");
        //跳过前 3 个数据
        list.stream().skip(3).forEach(s -> System.out.println(s));
    }

Java中的Stream流到底是啥?怎么用?

  • distinct
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅");
        list.add("陈大帅");
        list.add("陈大帅");
        list.add("陈大帅");
        list.add("陈小帅");
        list.add("陈小帅");
        list.add("陈小帅");
        list.add("陈小帅");
        list.add("小红");
        list.add("李四");
        list.add("陈四");
        //去重
        list.stream().distinct().forEach(s -> System.out.println(s));
    }

Java中的Stream流到底是啥?怎么用?

  • concat
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("陈大帅");
    list.add("陈小帅");
    list.add("小红");
    list.add("李四");
    list.add("陈四");
    List<String> list2 = new ArrayList<>();
    list2.add("a");
    list2.add("b");
    list2.add("c");
    //合并,如果类型不相同,类型就会变为共同的父类
    Stream.concat(list.stream(),list2.stream()).forEach(s -> System.out.println(s));
}

Java中的Stream流到底是啥?怎么用?

  • 将每个元素转换为另一种类型或形式
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅-20");
        list.add("陈小帅-19");
        list.add("小红-11");
        list.add("李四-12");
        list.add("陈四-13");
        list.stream().map(new Function<String, Integer>() {
            //第一个类型:流中原本的数据类型
            // 第二个类型:要转成之后的类型
            @Override
            public Integer apply(String s) {
                //apply的形参s:依次表示流里面的每一个数据
                //返回值:表示转换之后的数据
                String[] split = s.split("-");
                int age = Integer.parseInt(split[1]);
                return age;
            }
        }).forEach(age-> System.out.println(age));//内部类

        list.stream().map(s->Integer.parseInt(s.split("-")[1])).forEach(age-> System.out.println(age));//lambda
    }

Java中的Stream流到底是啥?怎么用?

3.终结方法

Stream方法中文解释作用
void forEach(Consumer action)遍历对每个元素执行指定的操作
long count()统计返回元素的个数
toArray()收集到数组返回一个包含所有元素的数组
collect(Collector collector)收集到集合返回一个包含所有元素的集合,可以指定集合的类型和特性
  • forEach
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅-20");
        list.add("陈小帅-19");
        list.add("小红-11");
        list.add("李四-12");
        list.add("陈四-13");

        // consumer的泛型:表示流中数据的类型
        list.stream().forEach(new Consumer<String>() {
            //accept方法的形参s:依次表示流里面的每一个数据
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        
        list.stream().forEach(s -> System.out.println(s));//lambda
    }
  • count
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅-20");
        list.add("陈小帅-19");
        list.add("小红-11");
        list.add("李四-12");
        list.add("陈四-13");
        //统计集合中的个数
        long count = list.stream().count();
        System.out.println(count);
    }

Java中的Stream流到底是啥?怎么用?

  • toArray
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅-20");
        list.add("陈小帅-19");
        list.add("小红-11");
        list.add("李四-12");
        list.add("陈四-13");
        //方式一:
        //收集集合中的数据到数组
        Object[] array = list.stream().toArray();
        System.out.println(Arrays.toString(array));
        
        //方式二:指定对于类型
        //IntFunction的泛型:具体类型的数组
        //toArray方法的参数:负责创建一个指定类型的数组
        //toArray方法的底层:会依次得到流里面的每一个数据,并把数据放到数组当中
        String[] strings = list.stream().toArray(new IntFunction<String[]>() {
            /**
             *
             * @param value 流中数据的个数,要跟数组长度保持一致
             * @return 具体类型的数组
             */
            @Override
            public String[] apply(int value) {
                return new String[value];
            }
        });
        System.out.println(Arrays.toString(strings));

        //方式三:使用 lambda 表达式
        String[] array1 = list.stream().toArray(value -> new String[value]);//lambda
        System.out.println(Arrays.toString(array1));
    }

Java中的Stream流到底是啥?怎么用?

  • collect

收集到List、Set中:

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("陈大帅");
        list.add("陈大帅");
        list.add("陈大帅");
        list.add("陈小帅");
        list.add("陈小帅");
        list.add("陈小帅");
        list.add("小红");
        list.add("李四");
        list.add("陈四");
        //收集到list中
        List<String> newList = list.stream().filter(s->s.startsWith("陈")).collect(Collectors.toList());
        System.out.println(newList);
        //收集到Set中,自动去重
        Set<String> set = list.stream().filter(s -> s.startsWith("陈")).collect(Collectors.toSet());
        System.out.println(set);
    }

收集到Map中:

    public static void main(String[] args) {
        List<String> list2 = new ArrayList<>();
        list2.add("陈大帅-20");
        list2.add("陈小帅-12");
        list2.add("小红-15");
        list2.add("李四-15");
        list2.add("陈四-38");
        //收集到Map中,键:姓名 值:年龄,注意:收集到Map中的时候,流中数据 key 不能重复
        Map<String,Integer> map = list2.stream()
                .filter(s->s.startsWith("陈"))
                /**
                 * toMap:参数一表示键的生成规则
                 *        参数二表示值的生成规则
                 * 参数一:
                 *      Function 泛型一:表示流中数据类型
                 *               泛型二:表示Map集合中key的数据类型
                 *      方法 apply 形参:依次表示流里面的每一个数据
                 *                返回值: Map 的 key
                 * 参数二:
                 *      Function 泛型一:表示流中数据类型
                 *               泛型二:表示Map集合中value的数据类型
                 *      方法 apply 形参:依次表示流里面的每一个数据
                 *                返回值:Map 的 value
                 */
                .collect(Collectors.toMap(new Function<String, String>() {
                    @Override
                    public String apply(String s) {
                         return s.split("-")[0];
                    }
                }, new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) {
                        return Integer.parseInt(s.split("-")[1]);
                    }
                }));
        System.out.println(map);

        //使用 lambda 表达式
        Map<String,Integer> map2 =
                list2.stream()
                .filter(s->s.startsWith("陈"))
                .collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[1])));
        System.out.println(map2);
    }

结果:

Java中的Stream流到底是啥?怎么用?

如果流中有重复的key,集合到Map时会报错,这跟集合到List不一样。

    public static void main(String[] args) {
        List<String> list2 = new ArrayList<>();
        list2.add("陈大帅-20");
        list2.add("陈大帅-21");
        list2.add("陈小帅-12");
        list2.add("小红-15");
        list2.add("李四-15");
        list2.add("陈四-38");

        //使用 lambda 表达式
        Map<String,Integer> map2 =
                list2.stream()
                .filter(s->s.startsWith("陈"))
                .collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[1])));
        System.out.println(map2);
    }

结果:

Java中的Stream流到底是啥?怎么用?

转载自:https://juejin.cn/post/7285184691418382355
评论
请登录