Java学习日志(十六): Stream流详解,Stream流综合案例 - Go语言中文社区

Java学习日志(十六): Stream流详解,Stream流综合案例


JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)

Stream

Stream:就是一个流式思想,可以把集合或数组,转化为一个Stream流,使用Stream流中的方法,来操作集合或数组

引言

现在,我们有一个集合,它有以下元素

list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");

需求:过滤掉集合中以张开头的元素,然后过滤掉字符串长度为3的元素,最后再遍历输出每个元素

使用传统的方式(循环遍历),对集合中的元素进行过滤,并遍历集合

public class Demo01 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合
        List<String> listZhang = new ArrayList<>();
        for (String name : list) {
            if(name.startsWith("张")){
                listZhang.add(name);
            }
        }
        //对listZhang集合中的元素进行过滤,只要名字长度为3的元素,存储到一个新的集合
        List<String> listThree = new ArrayList<>();
        for (String name : listZhang) {
            if(name.length()==3){
                listThree.add(name);
            }
        }
        //遍历listThree
        for (String name : listThree) {
            System.out.println(name);
        }
    }
}

这段代码中含有三个循环,每一个作用不同:

  1. 首先筛选所有姓张的人;
  2. 然后筛选名字有三个字的人;
  3. 后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。

所以就有了Lambda的衍生物Stream

示例:Stream可以使代码更加优雅

public class Demo02 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");



        //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合
        //对listZhang集合中的元素进行过滤,只要名字长度为3的元素,存储到一个新的集合
        //遍历listThree
        //把list转化为流
        /*list.stream()
                .filter(name->name.startsWith("张"))
                .filter(name->name.length()==3)
                .forEach(name->System.out.println(name));*/
        //方法引用
        list.stream()
                .filter(name->name.startsWith("张"))//过滤以张开头的元素
                .filter(name->name.length()==3)//过滤长度为3的元素
                .forEach(System.out::println);//遍历

    }
}

流式模型

Stream流:需要先创建一条流式模型,根据模型操作数据
在这里插入图片描述
注意:

  1. 这里的 filter 、 map 、 skip 都是在对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法 count 执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。
  2. Stream流只操作数据,并不保存数据。当一次操作结束以后,会产生一个新的Stream流,原来的Stream流会关闭。

获取Stream流的方式

java.util.stream.Stream<泛型> 是Java 8新加入的最常用的流接口

获取Stream流的方式

  1. 所有的Collection集合都可以通过stream默认方法获取流。
    default Stream<E> stream() :返回以此集合为源的顺序 Stream 。
    注意:此方法都是单列集合Collection中的方法,Map集合不能直接使用
  2. Stream接口的静态方法of可以获取数组对应的流
    static <T> Stream<T> of​(T... values) :传递可变参数,把可变参数转换为Stream流。
    注意:可变参数底层就是一个数组,所以也可以传递数组,创建Stream流

示例一:Collection集合获取Stream流

  1. list集合获取Stream流
//创建list集合
List<String> list = new ArrayList<>();
//使用List集合中的方法stream,把集合转化为流
Stream<String> stream1 = list.stream();
  1. set集合获取Stream流
//创建set集合
Set<String> set = new HashSet<>();
//使用set集合中的方法stream,把集合转化为流
Stream<String> stream2 = set.stream();
  1. Map集合获取Stream流(间接获取)
//第一种:转化为set或Colleciton集合来获取流
Map<String,String> map = new HashMap<>();
Set<String> keySet = map.keySet();//获取所有的键
Stream<String> stream3 = keySet.stream();

Collection<String> values = map.values();//获取所有的值
Stream<String> stream4 = values.stream();

//第二种:通过entry来获取流
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream5 = entries.stream();

示例二:数组获取Stream流

  1. 可变参数获取Stream流
//Stream接口的静态方法of创建stream流
Stream<String> stream1 = Stream.of("a", "b", "c", "d");

Stream<Integer> stream2 = Stream.of(1,2,3,4);
  1. 数组获取流
//可变参数底层就是一个数组,所以也可以传递数组,创建Stream流

String[] arr1 = {"a", "b", "c", "d"};
Stream<String> stream3 = Stream.of(arr1);

Integer[] arr2 = {1,2,3,4};
Stream<Integer> stream4 = Stream.of(arr2);

注意:以下写法错误,数组的数据类型必须使用包装类不能使用基本类型

int[] arr3 = {1,2,3,4,5};
Stream<int[]> stream5 = Stream.of(arr3);

常用方法

Stream流的方法分为两种方法:

  • 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调 用。本小节中,终结方法包括 count 和 forEach 方法
  • 非终结方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余 方法均为非终结方法。)

foreach:遍历

void forEach​(Consumer<? super T> action) 对此流的每个元素执行操作。
是一个终结方法,返回值不是Stream,不能再使用Stream中的方法了。
参数:函数式接口Consumer,所以可以使用lambda表达式,重写accept方法

作用:对Stream流进行遍历

示例:

public class Demo02 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
       	//方法引用
        stream.forEach(System.out::println);
    }
}

注意:若使用了两次Stream流,则会报IllegalStateException: stream has already been operated upon or closed异常。第二次使用Stream中的方法forEach,抛出了非法的状态异常,流是一个流式模型,流使用完毕之后,数据就流转到下一步,这时上一步的流就已经关闭,所以就不能再使用了,Stream流只能使用一次。

public class Demo02 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //第一次使用Stream流
        stream.forEach( s-> System.out.println(s));
        //第二次使用Stream流,出现异常!
        stream.forEach(System.out::println);
    }
}

filter:过滤

Stream<T> filter​(Predicate<? super T> predicate) 返回由与此给定谓词匹配的此流的元素组成的流。
参数:函数式接口Predicate,所以可以使用lambda表达式,重写test方法

作用:对Stream流中的元素进行过滤,形成一个新的Stream流

示例:对Stream流中的元素进行过滤,只要包含"羊羊",并对新Stream流进行遍历

public class Demo03 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //对Stream流中的元素进行过滤,只要包含"羊羊"
        Stream<String> streamYY = stream.filter(s -> s.contains("羊羊"));
        //遍历Stream
        streamYY.forEach(System.out::println);
    }
}

count:统计

long count() 返回此流中元素的数量。
是一个终结方法,返回值不是Stream,不能再使用Stream中的方法了

作用:统计Stream流中元素的个数

示例:

public class Demo04 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        long count = stream.count();
        System.out.println(count);//8
    }
}

limit:获取

Stream<T> limit​(long maxSize) 返回由此流的元素组成的流,截断长度不超过 maxSize。

作用:获取前几个元素,把元素存储到一个新的Stream流中

例如:

  • limit(3)获取流中前3个元素
  • limit(n)获取流中前n个元素

示例:获取Stream流中前5个元素,并遍历

public class Demo05 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");

        //获取Stream流中的前5个元素
        Stream<String> limitStream = stream.limit(5);
        //遍历
        limitStream.forEach(System.out::println);
    }
}

skip:跳过

Stream<T> skip​(long n) 在丢弃流的第一个 n元素后,返回由此流的其余元素组成的流。

作用:跳过前几个元素,把剩余的元素存储到一个新的Stream流中

例如:skip(3):跳过前三个元素

示例:跳过Stream流的前5个元素,并遍历

public class Demo06 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //跳过前五个元素
        Stream<String> stringStream = stream.skip(5);
        //遍历
        stringStream.forEach(System.out::println);
    }
}

map:映射

<R> Stream<R> map​(Function<? super T,​? extends R> mapper) 返回一个流,该流包含将给定函数应用于此流的元素的结果。
参数:函数式接口Function,所以可以使用lambda表达式,重写apply方法

作用:映射,把String流的数据类型转化为另外一种数据类型的Stream流

示例:使用map方法把String类型的Stream映射为Integer类型的Stream

public class Demo07 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stringStream = Stream.of("1", "2", "3", "4", "5");
        //使用map方法把String类型的Stream映射为Integer类型的Stream
        Stream<Integer> integerStream = stringStream.map((String s) -> {
            return Integer.parseInt(s);
        });
        //遍历
        integerStream.forEach(System.out::println);

    }
}

练习:使用map方法把String类型的姓名映射为Person类型的对象

public class Demo08 {
    public static void main(String[] args) {
        //创建一个存储String的Stream流
        Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六", "田七");
        //Stream<Person> personStream = stream.map(name-> new Person(name));
        Stream<Person> personStream = stream.map(Person::new);
        //遍历
        personStream.forEach(System.out::println);
    }
}

concat(静态方法):组合

static <T> Stream<T> concat​(Stream<? extends T> a, Stream<? extends T> b) 创建一个延迟连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。

作用:把两个Stream流合成一个

示例:使用Stream中的静态方法concat,把两个流合成一个新的流

public class Demo09 {
    public static void main(String[] args) {
        //创建一个Stream1流
        Stream<String> stream1 = Stream.of("张三", "李四", "王五", "赵六", "田七");
        //创建一个Stream2流
        Stream<String> stream2 = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //使用Stream中的静态方法concat,把两个流合成一个新的流
        Stream<String> stream = Stream.concat(stream1, stream2);
        //遍历
        stream.forEach(System.out::println);
    }
}

收集Stream结果

Stream流转化为集合

  1. 转化为List集合:stream.collect(Collectors.toList());
public class Demo01 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //转化为List集合:stream.collect(Collectors.toList());
        List<String> list = stream.collect(Collectors.toList());
        System.out.println(list);

        }
}
  1. 转化为Set集合:stream.collect(Collectors.toSet());
public class Demo01 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        
        Set<String> set = stream.collect(Collectors.toSet());
        System.out.println(set);
    }
}

Stream流转化为数组

转化为数组:stream.toArray()
返回值为Object类型

public class Demo02 {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懒羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //使用Stream流中的方法toArray
        Object[] arr = stream.toArray();
        for (Object o : arr) {
            System.out.println(o);
        }
    }
}

Stream流综合案例

需求:
现在有两个 ArrayList 集合存储队伍当中的多个成员

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Sakuraaaaaaa/article/details/104277720
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢