Stream流式思想概述

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

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

处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。


流水线中java编译 java 流水线模式_System

                                                                 

流水线中java编译 java 流水线模式_System_02

 

流水线中java编译 java 流水线模式_System_03

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

小结

Stream是流式思想,相当于工厂的流水线,对集合中的数据进行加工处理

获取 Stream流的两种方式

方式1 : 根据Collection获取流

首先, java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。

public interface Collection {

default Stream<E> stream()

}

public class Demo04GetStream {
    public static void main(String[] args) {
// 集合获取流
// 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();
        Vector<String> vector = new Vector<>();
// ...
        Stream<String> stream3 = vector.stream();
    }
}

public class Demo05GetStream {
    public static void main(String[] args) {
// Map获取流
        Map<String, String> map = new HashMap<>();
// ...
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

方式2 : Stream中的静态方法of获取流

由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:

public class Demo06GetStream {
    public static void main(String[] args) {
// Stream中的静态方法: static Stream of(T... values)
        Stream<String> stream6 = Stream.of("aa", "bb", "cc");
        String[] arr = {"aa", "bb", "cc"};
        Stream<String> stream7 = Stream.of(arr);
        Integer[] arr2 = {11, 22, 33};
        Stream<Integer> stream8 = Stream.of(arr2);
// 注意:基本数据类型的数组不行
        int[] arr3 = {11, 22, 33};
        Stream<int[]> stream9 = Stream.of(arr3);
    }
}

Stream常用方法

Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

方法名

方法作用

返回值类型

方法种类

count

统计个数

long

终结

forEach

逐一处理

void

终结

filter

过滤

Stream

函数拼接

limit

取用前几个

Stream

函数拼接

skip

跳过前几个

Stream

函数拼接

map

映射

Stream

函数拼接

concat

组合

Stream

函数拼接

终结方法 :返回值类型不再是 Stream 类型的方法,不再支持链式调用。

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

Stream的3个注意事项:

1. Stream只能操作一次

2. Stream方法返回的是新的流

3. Stream不调用终结方法,中间的操作不会执行

Stream 流的forEach方法

/**
 * @author WGR
 * @create 2020/3/26 -- 21:52
 */
public class StreamTest {

    @Test
    public void test(){
        List<String> strings = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
        strings.stream().forEach(System.out::print);  // 对象 + 实例方法
    }
}



流水线中java编译 java 流水线模式_System_04

Stream 流的count方法

Stream流提供 count 方法来统计其中的元素个数:

public class StreamTest {

    @Test
    public void test(){
        List<String> strings = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
        long count = strings.stream().count();
        System.out.println(count);  //6
    }
}

Stream 流的filter方法

filter用于过滤数据,返回符合过滤条件的数据

流水线中java编译 java 流水线模式_Test_05

 可以通过 filter 方法将一个流转换成另一个子集流。方法声明:

Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。

Stream流中的 filter 方法基本使用的代码如:

public class StreamTest {

    @Test
    public void test(){
        List<String> strings = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
        strings.stream().filter( s -> s.length() ==2 ).forEach(System.out::println);
    }
}


流水线中java编译 java 流水线模式_System_06

 在这里通过Lambda表达式来指定了筛选的条件:姓名长度为2个字。

Stream 流的limit方法

流水线中java编译 java 流水线模式_Test_07

 limit 方法可以对流进行截取,只取用前n个。方法签名:

Stream<T> limit(long maxSize);

参数是一个long型,如果集合当前长度大于参数则进行截取。否则不进行操作。基本使用:

public class StreamTest {

    @Test
    public void test(){
        List<String> strings = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
        strings.stream().limit(3).forEach(System.out::println);
    }
}



流水线中java编译 java 流水线模式_System_08

 Stream 流的skip方法

流水线中java编译 java 流水线模式_流水线中java编译_09

 如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:

Stream<T> skip(long n);

如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。基本使用:

public class StreamTest {

    @Test
    public void test(){
        List<String> strings = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
        strings.stream().skip(2).forEach(System.out::println);
    }
}

流水线中java编译 java 流水线模式_数据_10

Stream 流的map方法

流水线中java编译 java 流水线模式_Test_11

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

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

Stream流中的 map 方法基本使用的代码如:

@Test
    public void test(){
        Stream<String> stringStream = Stream.of("11", "22", "33");
        stringStream.map(Integer::parseInt).forEach(System.out::println);

    }

这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对象)。

Stream 流的sorted方法

如果需要将数据排序,可以使用 sorted 方法。方法签名:

Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

Stream流中的 sorted 方法基本使用的代码如:

@Test
    public void test(){
        Stream<String> stringStream = Stream.of( "22", "33","11");
        stringStream.map(Integer::parseInt).sorted( (o1,o2) -> o1 -o2 ).forEach(System.out::println);

    }

流水线中java编译 java 流水线模式_流水线中java编译_12

 这段代码中, sorted 方法根据元素的自然顺序排序,也可以指定比较器排序。

Stream 流的distinct方法

流水线中java编译 java 流水线模式_Test_13

 如果需要去除重复数据,可以使用 distinct 方法。方法签名:

Stream<T> distinct();

Stream流中的 distinct 方法基本使用的代码如:

@Test
    public void test(){
        Stream<String> stringStream = Stream.of( "11","22", "33","11");
        stringStream.map(Integer::parseInt).distinct().forEach(System.out::println);

    }



流水线中java编译 java 流水线模式_流水线中java编译_14

@Test
    public void test(){
        Stream.of(
                new Person("刘德华", 58),
                new Person("张学友", 56),
                new Person("张学友", 56),
                new Person("黎明", 52))
                .distinct()
                .forEach(System.out::println);
    }

/**
 * @author WGR
 * @create 2020/3/26 -- 22:07
 */
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

流水线中java编译 java 流水线模式_流水线中java编译_15

 自定义类型是根据对象的hashCode和equals来去除重复元素的。

Stream 流的match方法

如果需要判断数据是否匹配指定的条件,可以使用 Match 相关方法。方法签名:

boolean allMatch(Predicate<? super T> predicate);
boolean anyMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);

Stream流中的 Match 相关方法基本使用的代码如:

@Test
    public void test(){
        Stream<String> stringStream = Stream.of( "11","22", "33","11");
        boolean b = stringStream.map(Integer::parseInt).allMatch(i -> i > 10);
        System.out.println(b);   //true 全部大于十
        boolean b1 = stringStream.map(Integer::parseInt).anyMatch(i -> i < 10);
        System.out.println(b1);  // 没有比10小的   //false
        boolean b3 = stringStream.map(Integer::parseInt).noneMatch(i -> i < 20);
        System.out.println(b3);   // 没有比20小的    //false
    }

Stream 流的find方法

流水线中java编译 java 流水线模式_数据_16

 如果需要找到某些数据,可以使用 find 相关方法。方法签名:

Optional<T> findFirst();
Optional<T> findAny();

Stream流中的 find 相关方法基本使用的代码如:

@Test
    public void testFind(){
        Optional<Integer> first = Stream.of(5, 3, 6, 1).findFirst();
        System.out.println("first = " + first.get());   //first = 5
        Optional<Integer> any = Stream.of(5, 3, 6, 1).findAny();
        System.out.println("any = " + any.get());      // any = 5
    }

Stream 流的max和min方法

流水线中java编译 java 流水线模式_流水线中java编译_17

 如果需要获取最大和最小值,可以使用 max 和 min 方法。方法签名:

Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);

Stream流中的 max 和 min 相关方法基本使用的代码如:

@Test
    public void testFind(){
        Optional<Integer> max = Stream.of(5, 3, 6, 1).max( (o1,o2) -> o1 - o2); // 取升序的最后一个
        System.out.println("max = " + max.get());   
        Optional<Integer> min = Stream.of(5, 3, 6, 1).min( (o1,o2) -> o1 - o2); // 取升序的第一个
        System.out.println("min = " + min.get());      
    }



流水线中java编译 java 流水线模式_Test_18

 Stream 流的reduce方法

流水线中java编译 java 流水线模式_数据_19

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

T reduce(T identity, BinaryOperator<T> accumulator);

Stream流中的 reduce 相关方法基本使用的代码如:

@Test
    public void testFind(){
        Integer reduce = Stream.of(5, 3, 6, 1).reduce(0, Integer::sum);
        System.out.println(reduce);   //15
    }


流水线中java编译 java 流水线模式_System_20

 Stream 流的map和reduce组合使用

@Test
    public void testFind(){
        int totalAge = Stream.of(
                new Person("刘德华", 58),
                new Person("张学友", 56),
                new Person("郭富城", 54),
                new Person("黎明", 52))
                .map(Person::getAge)
                .reduce(0, Integer::sum);
        System.out.println("totalAge = " + totalAge);

        int maxAge = Stream.of(
                new Person("刘德华", 58),
                new Person("张学友", 56),
                new Person("郭富城", 54),
                new Person("黎明", 52))
                .map(Person::getAge)
                .reduce(0, Math::max);
        System.out.println("maxAge = " + maxAge);
    }


流水线中java编译 java 流水线模式_System_21



@Test
    public void testFind(){
        // 统计 数字2 出现的次数
        int count = Stream.of(1, 2, 2, 1, 3, 2)
                .map(i -> i == 2 ? 1 : 0)
                .reduce(0, Integer::sum);
        System.out.println("count = " + count);
    }

Stream 流的mapToInt

如果需要将Stream中的Integer类型数据转成int类型,可以使用 mapToInt 方法。方法签名:

IntStream mapToInt(ToIntFunction<? super T> mapper);

@Test
    public void testFind(){
        // Integer占用的内存比int多,在Stream流操作中会自动装箱和拆箱
        Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5});
        IntStream intStream = stream.mapToInt(Integer::intValue);
        int reduce = intStream
                .filter(i -> i > 3)
                .reduce(0, Integer::sum);
        System.out.println(reduce);

    }

Stream 流的concat方法

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

备注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。

该方法的基本使用代码如:

@Test
    public void testFind(){
        Stream<String> streamA = Stream.of("张三");
        Stream<String> streamB = Stream.of("李四");
        Stream<String> result = Stream.concat(streamA, streamB);
        result.forEach(System.out::println);
    }

流水线中java编译 java 流水线模式_数据_22