Stream流

目录

循环遍历的弊端 

流式思想概述

获取流

延迟方法

1.filter(过滤)

2.map(映射)

3.limit

4.skip

5.concat

6.distinct

7.sorted(排序)

终结方法

1.count(计数)

2.forEach

3.collect

案例

传统模式

Stream流的模式


说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。 

Stream流是用在集合上面

几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,典型的就是集合遍历。


循环遍历的弊端 


Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行 了对比说明。现在,我们仔细体会一下上例代码,可以发现:
for循环的语法就是“怎么做” for循环的循环体才是“做什么”
为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从 第一个到最后一个顺次处理的循环。前者是目的,后者是方式。
试想一下,如果希望对集合中的元素进行筛选过滤:
1. 将集合A根据条件一过滤为子集B; 2. 然后再根据条件二过滤为子集C。

import java.util.ArrayList;
import java.util.List;

/**
 * Created by asus on 2020/6/10.
 */
//现在有一个需求,就是1,找出名字姓张的,名字的长度是3,将名字输出到控制台
public class StreamTest {
    public static void getName()
    {
        List<String>list=new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        //一般的做法是使用多次for循环进行筛选
        List<String>list1=new ArrayList<>();
        List<String>list2=new ArrayList<>();
        for(String s:list)
        {
            if(s.startsWith("张"))
            {
                list1.add(s);
            }
        }
        for(String n:list1)
        {
            if(n.length()==3)
            {
                list2.add(n);
            }
        }
        for(String name:list2)
        {
            System.out.println(name);
        }


    }

    public static void main(String[] args) {
        getName();
    }




}

这段代码中含有三个循环,每一个作用不同:
1. 首先筛选所有姓张的人; 2. 然后筛选名字有三个字的人; 3. 后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循 环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。

Stream的更优写法

import java.util.ArrayList;
import java.util.List;

/**
 * Created by asus on 2020/6/10.
 */
//现在有一个需求,就是1,找出名字姓张的,名字的长度是3,将名字输出到控制台
public class StreamTest {
    public static void getName()
    {
        List<String>list=new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        //一般的做法是使用多次for循环进行筛选
        List<String>list1=new ArrayList<>();
        List<String>list2=new ArrayList<>();
        for(String s:list)
        {
            if(s.startsWith("张"))
            {
                list1.add(s);
            }
        }
        for(String n:list1)
        {
            if(n.length()==3)
            {
                list2.add(n);
            }
        }
        for(String name:list2)
        {
            System.out.println(name);
        }


        //这里我们可以使用Stream流的方式
        list.stream()
                .filter((name)->name.startsWith("张"))
                .filter((name)->name.length()==3)
                .forEach((name)-> System.out.println(name));
    }

    public static void main(String[] args) {
        getName();
    }




}

流式思想概述

整体来看,流式思想类似于工厂车间的“生产流水线”。


当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个“模型”步骤 方案,然后再按照方案去执行它。

这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模 型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。而右侧的数字 3是终结果。
这里的 filter 、 map 、 skip 都是在对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法 count 或者forEach执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性

当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换→执行操作获取想要的结 果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以 像链条一样排列,变成一个管道。

获取流

java.util.stream.Stream<T> 是Java 8新加入的常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式:

  1. 所有的 Collection 集合都可以通过 stream 默认方法获取流;
  2. Stream 接口的静态方法 of 可以获取数组对应的流。 
import java.util.*;
import java.util.stream.Stream;

/**
 * Created by asus on 2020/6/10.
 */
/*
    java.util.stream.Stream<T>是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
    获取一个流非常简单,有以下几种常用的方式:
        - 所有的Collection集合都可以通过stream默认方法获取流;
            default Stream<E> stream​()
        - Stream接口的静态方法of可以获取数组对应的流。
            static <T> Stream<T> of​(T... values)
            参数是一个可变参数,那么我们就可以传递一个数组
 */
public class GetStream {
    public static void main(String[] args) {
        //把集合转换为stream流
        List<String> list = new ArrayList<String>();
        Stream<String> stream1 = list.stream();

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

        Map<String,String> map = new HashMap<>();
        //获取键,存储到一个Set集合中
        Set<String> keySet = map.keySet();
        Stream<String> stream3= keySet.stream();

        //获取值,存储到一个Collection集合中
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();

        //获取键值对(键与值的映射关系entrySet)
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();

        //把数组转换为Stream流
        Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5, 6);
        //可变参数可以传递数组
        Integer[] arr = {1,2,3,4,5};
        Stream<Integer> stream7 = Stream.of(arr);
        String[] arr1 = {"a","b"};
        Stream<Object> stream8 = Stream.of(arr1);


    }
    
}

延迟方法

1.filter(过滤)

package com.qjx.functioninterface;

import java.util.stream.Stream;

/*
过滤筛选出偶数
 */
public class FilterTest {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7);
        Stream<Integer> stream1 = stream.filter(num -> num % 2 == 0);
        stream1.forEach(num-> System.out.println(num));
    }
}

2.map(映射)

(对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素)

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

import java.util.stream.Stream;

/**
 * Created by asus on 2020/6/11.
 */
/*
    Stream流中的常用方法_map:用于类型转换
    如果需要将流中的元素映射到另一个流中,可以使用map方法.
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
    Function中的抽象方法:
        R apply(T t);
 */
//这里我们将String类型的数据转换为整数类型的
public class StreamMap {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("1", "2", "3", "4");
        Stream<Integer> stream2 = stream1.map((String s) -> Integer.parseInt(s));
        stream2.forEach((Integer n)-> System.out.println(n));
    }
}

3.limit

只取前几个元素

package com.qjx.functioninterface;

import java.util.stream.Stream;
/*
只取前三个数
 */
public class LimitTest {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1","2","3","4","5");
        Stream<String> limit = stream.limit(3);
        limit.forEach(num-> System.out.println(num));

    }
}

4.skip

跳过几个元素

package com.qjx.functioninterface;

import java.util.stream.Stream;
/*
跳过前三个数字,只要后面两个
 */
/*
    Stream流中的常用方法_skip:用于跳过元素
    如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:
    Stream<T> skip(long n);
        如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
 */
public class SkipTest {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1","2","3","4","5");
        Stream<String> skip = stream.skip(3);
        skip.forEach(num-> System.out.println(num));

    }
}

5.concat

将两个流进行合并

package com.qjx.functioninterface;

import java.util.stream.Stream;
/*
    Stream流中的常用方法_concat:用于把流组合到一起
    如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat
    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
 */
public class ConcatTest {
    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
        Stream<Integer> stream2 = Stream.of(6, 7, 8, 9, 10);
        Stream<Integer> stream = Stream.concat(stream1, stream2);
        stream.forEach(num-> System.out.println(num));

    }
}

6.distinct

去除重复元素

package com.qjx.functioninterface;

import java.util.stream.Stream;

public class DistantTest {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 1, 2, 3, 4, 11, 1, 2);
        Stream<Integer> distinct = stream.distinct();
        distinct.forEach(num-> System.out.println(num));

    }
}

7.sorted(排序)

package com.qjx.functioninterface;

import java.util.stream.Stream;

public class SortedTest {
    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7);
        //正序
        Stream<Integer> sorted1 = stream1.sorted(((o1, o2) -> o1.compareTo(o2)));
        sorted1.forEach(num-> System.out.println(num));
        //结果是  1  2  3  4  5  6  7

        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6, 7);
        //倒序
        Stream<Integer> sorted2 = stream2.sorted(((o1, o2) -> o2.compareTo(o1)));
        sorted2.forEach(num-> System.out.println(num));
        //结果是  7  6  5  4  3  2  1


    }
}

终结方法

1.count(计数)

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

/**
 * Created by asus on 2020/6/11.
 */
/*
    Stream流中的常用方法_count:用于统计Stream流中元素的个数
    long count();
    count方法是一个终结方法,返回值是一个long类型的整数
    所以不能再继续调用Stream流中的其他方法了
 */
public class StreamCount {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        Stream<Integer> stream = list.stream();
        System.out.println(stream.count());
    }
}

2.forEach

    public static void main(String[] args) {
        //forEach方法用来遍历流中的数据
        Stream<String> stream = Stream.of("张三","李四","王五");
        stream.forEach(name-> System.out.println(name));
    }

3.collect

package com.qjx.functioninterface;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CollectTest {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7);
        List<Integer> collect = stream.collect(Collectors.toList());
        System.out.println(collect);
        //[1, 2, 3, 4, 5, 6, 7]
    }
}

案例

   现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        6. 根据姓名创建Person对象;存储到一个新集合中。
        7. 打印整个队伍的Person对象信息。

/**
 * Created by asus on 2020/6/11.
 */


public class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

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

传统模式

package com.itheima.demo03.Stream;

import java.util.ArrayList;

/*
    练习:集合元素处理(传统方式)
        现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        6. 根据姓名创建Person对象;存储到一个新集合中。
        7. 打印整个队伍的Person对象信息。
 */
public class Demo01StreamTest {
    public static void main(String[] args) {
        //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        ArrayList<String> one1 = new ArrayList<>();
        for (String name : one) {
            if(name.length()==3){
                one1.add(name);
            }
        }
        //2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        ArrayList<String> one2 = new ArrayList<>();
        for (int i = 0; i <3 ; i++) {
            one2.add(one1.get(i));//i = 0,1,2
        }

        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        //3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        ArrayList<String> two1 = new ArrayList<>();
        for (String name : two) {
            if(name.startsWith("张")){
                two1.add(name);
            }
        }
        //4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        ArrayList<String> two2 = new ArrayList<>();
        for (int i = 2; i <two1.size() ; i++) {
            two2.add(two1.get(i)); //i 不包含0 1
        }

        //5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        ArrayList<String> all = new ArrayList<>();
        all.addAll(one2);
        all.addAll(two2);

        //6. 根据姓名创建Person对象;存储到一个新集合中。
        ArrayList<Person> list = new ArrayList<>();
        for (String name : all) {
            list.add(new Person(name));
        }

        //7. 打印整个队伍的Person对象信息。
        for (Person person : list) {
            System.out.println(person);
        }
    }
}

Stream流的模式

package com.itheima.demo03.Stream;

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

/*
    练习:集合元素处理(Stream方式)
    将上一题当中的传统for循环写法更换为Stream流式处理方式。
    两个集合的初始内容不变,Person类的定义也不变。
 */
public class Demo02StreamTest {
    public static void main(String[] args) {
        //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        //2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);

        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        //3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        //4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);

        //5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        //6. 根据姓名创建Person对象;存储到一个新集合中。
        //7. 打印整个队伍的Person对象信息。
        Stream.concat(oneStream,twoStream).map(name->new Person(name)).forEach(p-> System.out.println(p));
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值