目录

1、聚合操作简介

 2、创建Stream流对象

3、Stream流的常用方法

4、Parallel Stream(并行流)

5、Stream拓展练习一_字符串流长度和字母排序

6、Stream拓展练习二_将流元素收集到map集合中

7、Stream拓展练习三_将流元素收集与面向对象的思想结合


1、聚合操作简介

  • Stream接口

        Stream接口,JDK8新增接口,该接口可以将集合、数组中的元素转换为Stream流的形式,并结合Lambda表达式的优势进一步简化集合、数组中元素的查找、过滤、转换等操作,这一新功能叫做聚合操作。

  • 在程序中,使用聚合操作没有绝对的语法规范,根据实际操作流程,主要可以分为以下三个步骤:
  1. 将原始集合和数据对象转换为Stream流;
  2. 对Stream流对象中的元素进行一系列的过滤、查找等中间操作(Intermediate Operations),然后返回一个Stream流对象;
  3. 对Stream流进行遍历、统计、收集等终结操作(Terminal operation),获取想要的结果。

注意:在进行聚合操作时,只改变Stream流对象中的数据,并不会改变原始集合和数组中的数据源。

 练习代码如下:

public class StreamExample01 {
    public static void main(String[] args) {
        //创建一个List集合对象
        ArrayList<String> list = new ArrayList<>();
        //add()方法向集合中添加元素
        list.add("张三");
        list.add("李四");
        list.add("张小明");
        list.add("张阳");

        //1.创建一个Stream流对象
        Stream<String> stream = list.stream();

        //2.对Stream流中的元素分别进行过滤、截取等操作
        Stream<String> stream1 = stream.filter(i -> i.startsWith("张"));
        Stream<String> stream2 = stream1.limit(2);

        //3.对Stream流中的数据进行终结操作,并进行遍历输出
        stream2.forEach(j-> System.out.println(j));

        System.out.println("=========");
        //通过链式表达式的形式完成聚合操作
        list.stream().filter(s->s.startsWith("张")).limit(2).forEach(System.out::println);
    }
}

运行结果如下:

 2、创建Stream流对象

        java提供了多种创建Stream流对象的方式,分别如下:

  • 所有的Collections集合都可以使用stream()静态方法获取Stream流对象;
  • Stream接口的of()静态方法可以获取基本类型包装类数组、引用类型数组和单个元素的Stream流对象。
  • Arrays数组工具类的stream()静态方法也可以获取数组元素的Stream流对象。

注意: 在JDK8中,只针对单列集合Collections接口提供了stream()静态方法获取Stream流对象,并未对Map集合提供相关看方法获取Stream流对象,所以想要用Map集合创建Stream流对象必须先通过Map集合的keySet()、values()、entrySet()等方法将Map集合转换为单列Set集合,然后再使用单列集合的stream()静态方法获取对应键、值集合的Stream流对象。

练习代码如下:

public class StreamExample2 {
    public static void main(String[] args) {
        /**
         * 数组获取stream流对象
         */
        String[] array={"apple", "banana", "orange"};     //创建一个字符串数组
        System.out.println("======数组获取第一种Stream流对象的方式========");
        List<String> list = Arrays.asList(array);         //将数组转换为List集合
        Stream<String> listStream = list.stream();        //1.使用集合对象的stream()静态方法创建Stream流对象
        listStream.forEach(System.out::println);

        System.out.println("======数组获取第二种Stream流对象的方式========");
        Stream<String> arrayStream1 = Stream.of(array);   //2.使用stream接口的of()静态方法创建Stream对象
        arrayStream1.forEach(System.out::println);

        System.out.println("======数组获取第三种Stream流对象的方式========");
        Stream<String> arrayStream2 = Arrays.stream(array);//3.使用Arrays数组工具类的stream()静态方法创建Stream流对象
        arrayStream2.forEach(System.out::println);


        /**
         * 单例集合
         * List类型获取stream流对象
         * 这里以ArrayList集合为例
         */
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("张");
        arrayList.add("王");
        arrayList.add("李");
        arrayList.add("赵");

        System.out.println("========ArrayList获取Stream流对象的第一种方法=======");
        Stream<String> listStream1 = arrayList.stream();    //使用集合对象的stream()静态方法创建Stream流对象
        listStream1.forEach(System.out::println);

        System.out.println("=====================误区========================");
        //注意:Stream<List<String>>,这里用of()获取的stream流的元素是List<String>类型,不是arrayList中的元素类型String。
        //所以想通过stream流对象对集合中的元素进行操作时,应该注意Stream流对象的of()静态方法和集合对象的Stream()静态方法的区别。
        Stream<List<String>> listStream2 = Stream.of(arrayList,list);
        //运行结果:// [张, 王, 李, 赵]// [apple, banana, orange]                                                                      // [张, 王, 李, 赵]// [apple, banana, orange]
        Stream<ArrayList<String>> listStream3 = Stream.of(arrayList);
        listStream2.forEach(System.out::println);
        System.out.println("================================================");
        listStream3.forEach(System.out::println);

        /**
         * 单例集合
         * Set类型获取stream流对象
         * 这里以HashSet集合为例
         */
        HashSet<String> set = new HashSet<>(); //参数化类型,只能容纳String元素
        HashSet hashSet = new HashSet();       //非参数化类型,可以容纳各种类型元素
        set.add("java");
        set.add("python");
        set.add("mysql");
        set.add("hadoop");
        set.add("java");

        System.out.println("==============HashSet获取Stream流对象=============");
        Stream<String> setStream = set.stream();    //使用集合对象的stream()静态方法创建Stream流对象
        setStream.forEach(System.out::println);


        /**
         * 在JDK8中,只针对单列集合Collections接口提供了stream()静态方法获取Stream流对象,并未对Map集合提供相关看方法
         * 获取Stream流对象,所以想要用Map集合创建Stream流对象必须先通过Map集合的keySet()、values()、entrySet()等方
         * 法将Map集合转换为单列Set集合,然后再使用单列集合的stream()静态方法获取对应键、值集合的Stream流对象。
         * 这里以HashMap集合为例
         */
        HashMap<String, Integer> hashMap = new HashMap<>();
        hashMap.put("A",1);
        hashMap.put("B",2);
        hashMap.put("C",3);
        hashMap.put("D",4);

        System.out.println("===============keySet().stream()方法==============");
        Stream<String> keySetStream = hashMap.keySet().stream();    //该方法返回一个包含Map中所有键的Stream流对象
        keySetStream.forEach(System.out::println);

        System.out.println("===============values.stream()方法================");
        Stream<Integer> valuesStream = hashMap.values().stream();   //该方法返回一个包含Map中所有值的Stream流对象
        valuesStream.forEach(System.out::println);

        System.out.println("===============entrySet().stream()方法================");
        //该方法返回一个包含Map中所有键值对(Entry)的Stream流对象。每个键值对都是一个Map.Entry对象,包含getKey()和getValue()方法。
        Stream<Map.Entry<String, Integer>> entrySetStream = hashMap.entrySet().stream();
        Stream<Map.Entry<String, Integer>> entrySetStream1 = hashMap.entrySet().stream();
        entrySetStream.forEach(System.out::println);
        System.out.println("=====================================================");
        entrySetStream1.forEach(s-> System.out.println(s.getKey()+":"+s.getValue()));

    }
}

3、Stream流的常用方法

         JDK8中为聚合操作中的Stream流对象提供了非常丰富的操作方法,这些方法被划分为中间操作和终结操作。

  • 终结操作:只要返回值类型不是Stream类型的就是终结操作,将会终结当前流模型
  • 中间操作:除了终结操作的操作都是中间操作。
Stream流的常用方法
方法声明功能描述
Stream <T> filter ( Predicate<? super T> predicate )将指定流对象中的元素进行过滤,并返回一个子流对象
Stream <R> map ( map(Faction<? super T,? extends R>mapper)将流中的元素按规则映射到另一个流中
Stream <T> distinct( )删除流中重复的元素
Stream <T> sorted( )将流中的数据按自然顺序进行排序
Stream <T> limit(long maxSize)截取流中元素的长度
Stream <T> skip(long n)丢弃流中前n个元素
static <T> Stream <T> concat (Stream <? extends T> a,Stream<? extends T> b将两个流对象合并成一个流
long count( )统计流元素的个数
R collect(Collect<?  super T,A ,R> collector )将流中的元素收集到一个容器中(如集合)
Object[ ] toArray( )将流中的元素收集到一个数组中
void forEach(Consumer<? super T> action)将流中的元素进行遍历

练习代码如下:

public class StreamExample03 {
    public static void main(String[] args) {

        String[] strings={"张三", "李四", "张小明", "张阳"};

        /**
         * 遍历
         * void forEach(Consumer<? super T> action)
         * Consumer函数式接口参数(可以是一个Lambda或方法引用)作为遍历动作
         * 不保证元素遍历过程在流中是被有序执行的。
         */
        //通过字符串源数据创建了一个Stream流对象
        Stream<String> forEachStream = Stream.of(strings);
        //使用forEach方法遍历stream流对象的元素(::为方法引用符)
        forEachStream.forEach(System.out::println);
        //stream.forEach(s-> System.out.println(s));
        System.out.println("========================");

        /**
         * 过滤
         * Stream <T> filter ( Predicate<? super T> predicate )
         * 将一个Stream流中的元素进行筛选转换成另一个子集流
         */
        //通过字符串源数据创建了一个Stream流对象
        Stream<String> filterStream1 = Stream.of(strings);
        Stream<String> filterStream2 = Stream.of(strings);
        filterStream1.filter(i->i.startsWith("张"))   //筛选以“张”开头的元素
                .filter(i->i.length()>2)             //筛选长度大于2个元素
                .forEach(System.out::println);       //对流元素进行遍历输出
        System.out.println("=========================");
        //筛选以“张”开头且长度大于2个元素
        filterStream2.filter(i->i.startsWith("张") && i.length()>2)
                .forEach(System.out::println);
        System.out.println("=========================");

        /**
         * 映射
         * Stream <R> map ( map(Faction<? super T,? extends R>mapper)
         * 将流对象中的元素通过特定的规则进行修改然后映射为另一个流对象
         */
        Stream<String> mapStream = Stream.of("a1", "a2", "b", "c1", "c2");
        mapStream.filter(s->s.startsWith("c"))  //筛选出流中以“c”开头的元素
                .map(String::toUpperCase)       //对流元素进行映射,将全部字符改为大写
                .sorted()                       //对流元素进行排序
                .forEach(System.out::println);  //对流元素进行遍历输出
        System.out.println("=========================");

        /**
         * 截取和跳过
         * Stream <T> limit(long maxSize)
         * Stream <T> skip(long n)
         */
        Stream<String> limitStream = Stream.of(strings);
        limitStream.skip(1)
                .limit(2)
                .forEach(System.out::println);
        System.out.println("=========================");

        /**
         * 收集
         * R collect(Collect<? super T,A ,R> collector )
         */
        Stream<String> collectStream1 = Stream.of(strings);
        //通过collect()方法进行终结操作,将流元素收集到一个List集合中
        List<String> list = collectStream1.filter(i -> i.startsWith("张")).collect(Collectors.toList());
        System.out.println(list);

        Stream<String> collectStream2 = Stream.of(strings);
        //通过collect()方法进行终结操作,将流元素使用“and”收集一个字符串中
        String string = collectStream2.filter(i -> i.startsWith("张")).collect(Collectors.joining("and"));
        System.out.println(string);

    }
}

4、Parallel Stream(并行流)

  • 上文中创建的流都是串行流(Serial Stream),即将源数据转换为一个流对象,然后在单线程下执行聚合操作的流,也就是单一管道留流。
  •  而JDK8中针对大批量的数据处理还提供了一个并行流(Parallel Stream),并行流就是将源数据分为多个子流对象进行多线程操作(即多个管道流),然后将处理后的结果再汇总为一个流对象。
  • 使用Stream并行流在一定程度上可以提升程序的执行效率,但是在多线程执行就会出现线程安全这个大问题,所以为了能够在聚合操作中使用Stream并行流中,前提是要执行操作的数据源在并行执行过程中不被修改。
  • 怎样创建并行流?

        JDK8中提供了两种创建并行流的方式。如下所示:

  1. Collection集合接口的parallelStream()方法直接将集合类型的源数据转变为Stream并行流
  2. BaseStream接口中的parallel()方法将Stream串行流转变为Stream并行流。此外,BaseStream接口还提供了isParallel()方法判断当前Stream流对象是否为并行流,返回值为boolean类型。

练习代码如下:

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("张三", "李四", "张小明", "张阳");
        //1.直接使用Collection接口的parallelStream()创建并行流
        Stream<String> parallelStream1 = list.parallelStream();
        System.out.println(parallelStream1.isParallel());    //输出结果为true

        Stream<String> stream = Stream.of("张三", "李四", "张小明", "张阳");
        //2.使用BaseStream接口的parallel()方法将串行流转变为并行流
        Stream<String> parallelStream2 = stream.parallel();
        System.out.println(parallelStream2.isParallel());    //输出结果为true
    }
}

5、Stream拓展练习一_字符串流长度和字母排序

 示例代码如下:

public class StreamDemo04 {
    public static void main(String[] args) {
        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>();

        list.add("linqingxia");
        list.add("zhangmanyu");
        list.add("wangzuxian");
        list.add("liuyan");
        list.add("zhangmin");
        list.add("zhangwuji");

        //需求1:按照字母顺序把数据在控制台输出
//        list.stream().sorted().forEach(System.out::println);

        //需求2:按照字符串长度把数据在控制台输出
//        list.stream().sorted((s1, s2) -> s1.length() - s2.length()).forEach(System.out::println);
        
        //需求3:按照字符串长度和字典顺序升序把数据在控制台输出
        list.stream().sorted((s1,s2) -> {
            int num = s1.length()-s2.length();    
            int num2 = num==0?s1.compareTo(s2):num; //compareTo()按字典顺序排序
            return num2;
        }).forEach(System.out::println);
    }
}

6、Stream拓展练习二_将流元素收集到map集合中

示例代码如下:

public class CollectDemo {
    public static void main(String[] args) {
        //创建List集合对象
        List<String> list = new ArrayList<String>();
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");

        /*
        //需求1:得到名字为3个字的流
        Stream<String> listStream = list.stream().filter(s -> s.length() == 3);

        //需求2:把使用Stream流操作完毕的数据收集到List集合中并遍历
        List<String> names = listStream.collect(Collectors.toList());
        for(String name : names) {
            System.out.println(name);
        }
        */

        //创建Set集合对象
        Set<Integer> set = new HashSet<Integer>();
        set.add(10);
        set.add(20);
        set.add(30);
        set.add(33);
        set.add(35);

        /*
        //需求3:得到年龄大于25的流
        Stream<Integer> setStream = set.stream().filter(age -> age > 25);

        //需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
        Set<Integer> ages = setStream.collect(Collectors.toSet());
        for(Integer age : ages) {
            System.out.println(age);
        }
        */


        //定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
        String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33", "柳岩,25"};

        //需求5:得到字符串中年龄数据大于28的流
        Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);

        //需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
        Map<String, Integer> map = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));

        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            Integer value = map.get(key);
            System.out.println(key + "," + value);
        }

    }
}

7、Stream拓展练习三_将流元素收集与面向对象的思想结合

示例代码如下:

Actor.class

/*
    演员类
 */
public class Actor {
    private String name;

    public Actor(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

StreamTest.class

public class StreamTest {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> manList = new ArrayList<String>();
        manList.add("周润发");
        manList.add("成龙");
        manList.add("刘德华");
        manList.add("吴京");
        manList.add("周星驰");
        manList.add("李连杰");


        ArrayList<String> womanList = new ArrayList<String>();
        womanList.add("林心如");
        womanList.add("张曼玉");
        womanList.add("林青霞");
        womanList.add("柳岩");
        womanList.add("林志玲");
        womanList.add("王祖贤");

        /*
        //男演员只要名字为3个字的前三人
        Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);

        //女演员只要姓林的,并且不要第一个
        Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);

        //把过滤后的男演员姓名和女演员姓名合并到一起
        Stream<String> stream = Stream.concat(manStream, womanStream);

        //把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
//        stream.map(Actor::new).forEach(System.out::println);
        stream.map(Actor::new).forEach(p -> System.out.println(p.getName()));
        */

        Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3),
                womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).
                forEach(p -> System.out.println(p.getName()));
    }
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐