带你看看JDK8新特性:Stream流

相信兄弟萌在学习时碰到过这样的代码吧:

List<String> list = new ArrayList();
list.stream().forEach(a-> System.out.println(a));

我相信大家初次见到这种代码跟我一样,这 TM 是个啥?能看出来有一个 Lambda 表达式,另外这个 stream 是个啥?

有没有一点像 Steam,游戏下载平台,可以玩吃鸡、CSGO,扯远了,两者毫无关系。

下面我们来揭开 stream 的秘密,Go!

1.集合处理数据的弊端

欸,不是讲 stream 吗?怎么讲到集合的处理数据了。别急,我们从集合处理数据的弊端来引入 stream。

当我们需要对集合中的元素进行操作时,除了必要的添加、删除、获取外,最典型的就是集合遍历。我们来体验集合操作数据的弊端,需求如下:

一个 ArrayList 集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰。

需求:

  1. 拿到所有姓张的
  2. 拿到名字长度为 3 个字的
  3. 打印这些数据
public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");

        //1.拿到所有姓张的
        ArrayList<String> zhangList = new ArrayList<String>(); //{"张无忌","张强","张三丰"}
        for (String name : list) {
            if(name.startsWith("张")){
                zhangList.add(name);
            }
        }
    
        //2.拿到名字长度为3个字的
        ArrayList<String> threeList = new ArrayList<String>();//{"张无忌","张三丰"}
        for (String name : zhangList) {
            if(name.length() == 3){
                threeList.add(name);
            }
        }
    
        //3.打印这些数据              //{"张无忌","张三丰"}
        for (String name : threeList) {
            System.out.println(name);
        }
    }

可以看出:每个需求都要遍历一次集合,还要搞一个新集合来装数据,非常麻烦

2.Stream 流介绍

Stream 流不是一种数据结构,不保存数据,而是对集合中的数据进行加工处理。Stream 流式思想类似于工厂车间的 “生产流水线”。

注意:Stream 流和 IO 流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统 IO 流的固有印象

在这里插入图片描述
下面我们来看看借助 Stream 流来改造上面的代码:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
        list.stream()
                .filter((s)->{
                    return s.startsWith("张");
                })
                .filter((s)->{
                    return s.length() == 3;
                })
                .forEach((s)->{
                    System.out.println(s);
                });
    }

3.获取 Stream 流的两种方式

获取一个流非常简单,有以下两种常用的方式:

  1. 所有的 Collection 集合都可以通过 .stream( ) 方法来获取流
  2. 使用 Stream 接口的 .of( ) 静态方法,可以获取流
public static void main(String[] args) {
        //方式1:根据Collection获取流
        //Collection接口中有一个默认的方法:default Stream<E> stream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set  = new HashSet<>();
        Stream<String> stream2 = set.stream();

        //方式2:Stream中的静态方法of获取流,应用场景:放数组
        //static<T> Stream<T> of(T... values) {
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");

        String[] strs = {"aa","bb","cc"};
        Stream<String> stream4 = Stream.of(strs);


        //基本类型的数组行不行?  这是不行的,会将整个数组看作一个元素进行操作
        int[] arr = {11,22,33};
        //这里流操作的类型是 int[],将整个数组看作一个元素来操作
        Stream<int[]> stream5 = Stream.of(arr);
    }

4.Stream 常用方法和注意事项

在这里插入图片描述
终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。常见的终结方法有:

  1. count
  2. foreach
  3. anyMatch & allMatch & noneMatch
  4. findAny & findFirst
  5. reduce
  6. max & min聚合方法都是终止操作

非终结方法:又叫函数拼接方法,值返回值类型仍然是 Stream 类型的方法,支持链式调用(除了终结方法外,其与方法均为非终结方法)。

Stream 注意事项

  1. Stream 只能操作一次
  2. Stream 方法返回的是新的流
  3. Stream 不调用终结方法,中间的操作不会执行
public static void main(String[] args) {
        Stream<String> stream = Stream.of("aa", "bb", "cc");
        //1. Stream 只能操作一次
        //long count = stream.count(); //执行成功
        //long count2 = stream.count();//执行报错

        //2. Stream 方法返回的是新的流
        //Stream<String> limit = stream.limit(1);
        //两个流不是同一个对象
        //System.out.println("stream"+stream);
        //System.out.println("limit"+limit);

        //3.Stream 不调用终结方法,中间的操作不会执行
        //这里只有调用了 count()(终结方法),中间的操作才会执行
        stream.filter((s)->{
            System.out.println(s);
            return true;
        }).count();
        
    }

5.Stream API

Stream API 能让我们快速完成许多复杂的操作,如:筛选、切片、映射、查找、去除重复、统计、匹配和归约

由于 Stream API 太多了,我们这里只重点讲几个常见的。

forEach

forEach 用来遍历流中的数据。

@Test
public void testForEach(){
    List<String> list  = new ArrayList<>();
    Collections.addAll(list,"Mary","Lucy","James","Johnson","Steve");

    list.stream()
            .forEach((s)->{
                System.out.println(s);
            });
}

count

count 用来统计其中元素的个数,该方法返回一个 long 值代表元素个数。

@Test
public void testCount(){
    List<String> list  = new ArrayList<>();
    Collections.addAll(list,"Mary","Lucy","James","Johnson","Steve");

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

}

filter

filter 用于过滤数据,返回符合过滤条件的数据。
在这里插入图片描述

@Test
public void testFilter(){
    List<String> list  = new ArrayList<>();
    Collections.addAll(list,"Mary","Lucy","James","Johnson","Steve");

    //得到名字长度为4个字的人
    list.stream()
            .filter((s)->{
                return s.length() == 4;
            })
            .forEach((s)->{
                System.out.println(s);
            });
}

map

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

在这里插入图片描述

@Test
public void testMap(){
    Stream<String> original = Stream.of("11", "22", "33");
		
    //Map 可以将一种类型的流转换成另一种类型的流
    //将 Stream 流汇总的字符串转成 Integer
    Stream<Integer> integerStream = original.map((s) -> {
        return Integer.parseInt(s);
    });

}

distinct

distinct 用来去除集合中重复的数据。
在这里插入图片描述

@Test
public void testDistinct(){
    Stream<Integer> stream = Stream.of(22,33,22,11,33);
    /*stream.distinct()
        .forEach((s)->{
                System.out.println(s);
        });*/
    Stream<String> stream1 = Stream.of("aa", "bb", "aa", "bb", "cc");
    stream1.distinct().forEach(s-> System.out.println(s));
 }

/**
 * distinct 对自定义对象去除重复(重写hashcode和equals,否则不会去重)
 */
@Test
public void testDistinct2(){
      Stream<Person> stream = Stream.of(
             new Person("貂蝉", 18),
             new Person("杨玉环", 20),
             new Person("杨玉环", 20),
             new Person("西施", 16),
             new Person("西施", 16),
             new Person("王昭君", 25)
      );
      stream.distinct().forEach(System.out::println);
    }

reduce

如果需要将所有数据归纳得到一个数据,可以使用 reduce 方法。

@Test
public void testReduce(){
    //T reduce(T identity, BinaryOperator<T> accumulator);
    //T identity:默认值
    //BinaryOperator<T> accumulator:对数据进行处理的方式
    /*Integer reduce = Stream.of(4, 5, 3, 9).reduce(
                0, (x, y) -> {
                    //x=0, y=4
                    //x=4, y=5
                    //x=9, y=3
                    //x=12, y=9
                    System.out.println("x="+x+", y="+y);
                    return x + y;
                }
    );
    System.out.println("reduce="+reduce); //21*/

    //获取最大值
    Integer max = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
        return x > y ? x : y;
    });
    System.out.println("最大值:"+max);
}

在这里插入图片描述

6.收集 Stream 流中的结果

对流操作完成之后,如果需要将流的结果保存到数据或集合中,可以收集流中的数据

6.1 收集到集合中

Stream 流提供 collect 方法,其参数需要一个 java.util.stream.Collector<T,A,R> 接口对象来指定收集到哪种集合。java.util.Collectors 类提供一些方法,可以作为 Collector 接口的实例:

//这里转换的 list 是 ArrayList,如果想要转换成特定的 list,需要使用 toCollection 方法
public static <T> Collector<T,?,List<T>> toList( ):转换为 List 集合

//这里转换的 set 是 HashSet,如果需要特别指定 set,那么需要使用 toCollection 方法
public static <T> Collector<T,?,List<T>> toSet( ):转换为 Set 集合
@Test
public void testStreamToCollection(){
    Stream<String> stream = Stream.of("aa", "bb", "cc");
    //将流中的数据收集到集合中
    /*List<String> list = stream.collect(Collectors.toList());
    System.out.println("list = "+list);*/   //list = [aa, bb, cc]

    /*Set<String> set = stream.collect(Collectors.toSet());
    System.out.println("set = "+set);  //set = [aa, bb, cc]*/

    //收集到指定的集合中ArrayList
    //如果想要转换成特定的list,需要使用toCollection方法
    LinkedList<String> collect = stream.collect(Collectors.toCollection(LinkedList::new));
}

6.1 收集到数组中

//将流中的数据收集到数组中
@Test
public void testStreamToArray(){
    Stream<String> stream = Stream.of("aa", "bb", "cc");
    //转成object数组不方便
    Object[] objects = stream.toArray();
    //String[]
    String[] strings = stream.toArray(String[]::new);
    for (String string : strings) {
        System.out.println("string = "+string);
    }
}

版权声明:本文为qq_37205350原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。