:star: Stream流

将数据变成流水线可以进行中间方法和最终方法

1. 初识Stream

1
2
3
4
5
6
7
8
9
10
11
public class StreamDemo {
public static void main(String[] args) {
//集合的批量添加
ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));

//Stream流
list1.stream().filter(s->s.startsWith("张"))
.filter(s->s.length() == 3)
.forEach(s-> System.out.println(s));
}
}

2. Stream流在各个数据类型中的应用

2.1数组

  • Arrays. stream

    1
    2
    3
    4
    5
    int[] array01 = {1,2,3,4,5,6,7,8,9};
    String[] array02 = {"1", "2", "3", "4", "5"};

    Arrays.stream(array01).forEach(System.out::println);
    Arrays.stream(array02).forEach(System.out::println);
  • Stream. of

    需要注意 Stram 中的静态方法of只能传递引用数据类型

    of中的形参数据类型需要一样

    1
    2
    3
    4
    5
    int[] array01 = {1,2,3,4,5,6,7,8,9};
    String[] array02 = {"1", "2", "3", "4", "5"};

    Stream.of(array01).forEach(System.out::println); //[I@404b9385
    Stream.of(array02).forEach(System.out::println);

2.2集合

  • 单列集合

    1
    2
    list.stream()
    set.stream()
  • 双列集合

    1
    2
    hm.keySet().stream()
    hm.entrySet().stream()

3. Stream流中间方法

  • 中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
  • 修改Stream流中的数据,不会影响原来集合或者数组中的数据

3.1 常见方法

方法名 说明
Stream filter(Predicate predicate) 用于对流中的数据进行过滤
Stream limit(long maxSize) 返回此流中的元素组成的流,截取前指定参数个数的数据
Stream skip(long n) 跳过指定参数个数的数据,返回由该流的剩余元素组成的流
static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流
Stream distinct() 返回由该流的不同元素(根据Object.equals(Object) )组成的流

3.2 代码演示

  • filter代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class MyStream3 {
    public static void main(String[] args) {
    // Stream<T> filter(Predicate predicate):过滤
    // Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值

    ArrayList<String> list = new ArrayList<>();
    list.add("张三丰");
    list.add("张无忌");
    list.add("张翠山");
    list.add("王二麻子");
    list.add("张良");
    list.add("谢广坤");

    //filter方法获取流中的 每一个数据.
    //而test方法中的s,就依次表示流中的每一个数据.
    //我们只要在test方法中对s进行判断就可以了.
    //如果判断的结果为true,则当前的数据留下
    //如果判断的结果为false,则当前数据就不要.
    // list.stream().filter(
    // new Predicate<String>() {
    // @Override
    // public boolean test(String s) {
    // boolean result = s.startsWith("张");
    // return result;
    // }
    // }
    // ).forEach(s-> System.out.println(s));

    //因为Predicate接口中只有一个抽象方法test
    //所以我们可以使用lambda表达式来简化
    // list.stream().filter(
    // (String s)->{
    // boolean result = s.startsWith("张");
    // return result;
    // }
    // ).forEach(s-> System.out.println(s));

    list.stream().filter(s ->s.startsWith("张")).forEach(s-> System.out.println(s));

    }
    }
  • limit&skip代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class StreamDemo02 {
    public static void main(String[] args) {
    //创建一个集合,存储多个字符串元素
    ArrayList<String> list = new ArrayList<String>();

    list.add("林青霞");
    list.add("张曼玉");
    list.add("王祖贤");
    list.add("柳岩");
    list.add("张敏");
    list.add("张无忌");

    //需求1:取前3个数据在控制台输出
    list.stream().limit(3).forEach(s-> System.out.println(s));
    System.out.println("--------");

    //需求2:跳过3个元素,把剩下的元素在控制台输出
    list.stream().skip(3).forEach(s-> System.out.println(s));
    System.out.println("--------");

    //需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
    list.stream().skip(2).limit(2).forEach(s-> System.out.println(s));
    }
    }
  • concat&distinct代码演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class StreamDemo03 {
    public static void main(String[] args) {
    //创建一个集合,存储多个字符串元素
    ArrayList<String> list = new ArrayList<String>();

    list.add("林青霞");
    list.add("张曼玉");
    list.add("王祖贤");
    list.add("柳岩");
    list.add("张敏");
    list.add("张无忌");

    //需求1:取前4个数据组成一个流
    Stream<String> s1 = list.stream().limit(4);

    //需求2:跳过2个数据组成一个流
    Stream<String> s2 = list.stream().skip(2);

    //需求3:合并需求1和需求2得到的流,并把结果在控制台输出
    // Stream.concat(s1,s2).forEach(s-> System.out.println(s));

    //需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
    Stream.concat(s1,s2).distinct().forEach(s-> System.out.println(s));
    }
    }

4. Stream流终结方法

终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作

4.1 常见方法

方法名 说明
void forEach(Consumer action) 对此流的每个元素执行操作
long count() 返回此流中的元素数
toArray(有参/空参) 创建一个数组,并且将流中的数据放入到数组中
collect 收集方法,将数据变成集合

4.2 代码演示

  • forEach

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //  void forEach(Consumer action):对此流的每个元素执行操作
    // Consumer接口中的方法void accept(T t):对给定的参数执行此操作
    //在forEach方法的底层,会循环获取到流中的每一个数据.
    //并循环调用accept方法,并把每一个数据传递给accept方法
    //s就依次表示了流中的每一个数据.
    //所以,我们只要在accept方法中,写上处理的业务逻辑就可以了.
    list.stream().forEach(
    new Consumer<String>() {
    @Override
    public void accept(String s) {
    System.out.println(s);
    }
    }
    );

    System.out.println("====================");
    //lambda表达式的简化格式
    //是因为Consumer接口中,只有一个accept方法
    list.stream().forEach(
    (String s)->{
    System.out.println(s);
    }
    );
    System.out.println("====================");
    //lambda表达式还是可以进一步简化的.
    list.stream().forEach(s->System.out.println(s));
  • count

    1
    Long l = list.stream().count()
  • toArray()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ArrayList<String> list = new ArrayList<>();
    list.add("张三丰");
    list.add("张无忌");
    list.add("张翠山");
    list.add("王二麻子");
    list.add("张良");
    list.add("谢广坤");


    String[] arr = list.stream().toArray(new IntFunction<String[]>() {
    @Override
    public String[] apply(int value) {
    return new String[value];
    }
    });

    System.out.println(Arrays.toString(arr));


    String[] arr2 = list.stream().toArray(value -> new String[value]);
    System.out.println(Arrays.toString(arr2));

  • collect

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    package com.itheima.a01mystream;

    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

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

    /*
    collect(Collector collector) 收集流中的数据,放到集合中 (List Set Map)

    注意点:
    如果我们要收集到Map集合当中,键不能重复,否则会报错
    */

    ArrayList<String> list = new ArrayList<>();
    Collections.addAll(list, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
    "张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");


    //收集List集合当中
    //需求:
    //我要把所有的男性收集起来
    List<String> newList1 = list.stream()
    .filter(s -> "男".equals(s.split("-")[1]))
    .collect(Collectors.toList());
    //System.out.println(newList1);


    //收集Set集合当中
    //需求:
    //我要把所有的男性收集起来
    Set<String> newList2 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
    .collect(Collectors.toSet());
    //System.out.println(newList2);


    //收集Map集合当中
    //谁作为键,谁作为值.
    //我要把所有的男性收集起来
    //键:姓名。 值:年龄
    Map<String, Integer> map = list.stream()
    .filter(s -> "男".equals(s.split("-")[1]))
    /*
    * toMap : 参数一表示键的生成规则
    * 参数二表示值的生成规则
    *
    * 参数一:
    * Function泛型一:表示流中每一个数据的类型
    * 泛型二:表示Map集合中键的数据类型
    *
    * 方法apply形参:依次表示流里面的每一个数据
    * 方法体:生成键的代码
    * 返回值:已经生成的键
    *
    *
    * 参数二:
    * Function泛型一:表示流中每一个数据的类型
    * 泛型二:表示Map集合中值的数据类型
    *
    * 方法apply形参:依次表示流里面的每一个数据
    * 方法体:生成值的代码
    * 返回值:已经生成的值
    *
    * */
    .collect(Collectors.toMap(new Function<String, String>() {
    @Override
    public String apply(String s) {
    //张无忌-男-15
    return s.split("-")[0];
    }
    },
    new Function<String, Integer>() {
    @Override
    public Integer apply(String s) {
    return Integer.parseInt(s.split("-")[2]);
    }
    }));


    Map<String, Integer> map2 = list.stream()
    .filter(s -> "男".equals(s.split("-")[1]))
    .collect(Collectors.toMap(
    s -> s.split("-")[0],
    s -> Integer.parseInt(s.split("-")[2])));

    System.out.println(map2);


    }
    }

5.Stream流综合训练

  • 案例需求

    现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作

    • 男演员只要名字为3个字的前三人
    • 女演员只要姓林的,并且不要第一个
    • 把过滤后的男演员姓名和女演员姓名合并到一起
    • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据

    演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

  • 代码实现

    演员类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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;
    }
    }

    测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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);

    // 将流中的数据封装成Actor对象之后打印
    stream.forEach(name -> {
    Actor actor = new Actor(name);
    System.out.println(actor);
    });
    }
    }

:star: 方法引用

把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体

image-20240417101341032

1. 样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.itheima.a01myfunction;


import java.util.Arrays;

public class FunctionDemo1 {
public static void main(String[] args) {
//需求:创建一个数组,进行倒序排列
Integer[] arr = {3, 5, 4, 1, 6, 2};
//匿名内部类

/* Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});*/



//lambda表达式
//因为第二个参数的类型Comparator是一个函数式接口
/* Arrays.sort(arr, (Integer o1, Integer o2)->{
return o2 - o1;
});*/

//lambda表达式简化格式
//Arrays.sort(arr, (o1, o2)->o2 - o1 );


//方法引用
//1.引用处需要是函数式接口
//2.被引用的方法需要已经存在
//3.被引用方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
//4.被引用方法的功能需要满足当前的要求

//表示引用FunctionDemo1类里面的subtraction方法
//把这个方法当做抽象方法的方法体
Arrays.sort(arr, FunctionDemo1::subtraction); // 类名+“::”+方法名

System.out.println(Arrays.toString(arr));

}



//可以是Java已经写好的,也可以是一些第三方的工具类
public static int subtraction(int num1, int num2) {
return num2 - num1;
}

}

2. 方法引用的分类

2.1 引用静态方法

  • 格式:类名::静态方法
  • 范例:Integet::parseInt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.yyh.funcationdemo;

import java.util.ArrayList;
import java.util.Collections;

public class Test01 {

public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"1", "2", "3", "4", "5");

//不使用方法引用
list.stream().map(s -> Integer.parseInt(s));

//使用方法引用
list.stream().map(Integer::parseInt);

}
}

2.2 引用成员方法

  • 格式:对象::成员方法
  • 其他类:其他类对象::方法名
  • 本类:this::方法名
  • 父类:super::方法名

2.2.1 引用其他类对象

其他类对象::方法名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.yyh.funcationdemo;

import java.util.ArrayList;
import java.util.function.Predicate;

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

ArrayList<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张无忌");
list.add("张翠山");
list.add("王二麻子");
list.add("张良");
list.add("谢广坤");

//方法引用
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张") && s.length() == 3;
}
}).forEach(System.out::println);

// 其他类引用
list.stream().filter(Operation :: stringJudge)
.forEach(System.out::println);

}




}


class Operation{

public static boolean stringJudge(String s) {
return s.startsWith("张") && s.length() == 3;
}

}

2.2.2 引用本类对象

注意静态中是没有this关键字的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.yyh.funcationdemo;

import java.util.ArrayList;
import java.util.function.Predicate;

public class Test03 {
public void main(String[] args) {

ArrayList<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张无忌");
list.add("张翠山");
list.add("王二麻子");
list.add("张良");
list.add("谢广坤");


//静态方法中是没有this关键字的
list.stream().filter(this::stringJudge)
.forEach(System.out::println);


}

public boolean stringJudge(String s) {
return s.startsWith("张") && s.length() == 3;
}


}

2.3 引用构造方法

  • 格式:类名::new
  • 范例:Student::new

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class FunctionDemo4 {
public static void main(String[] args) {
/*
方法引用(引用构造方法)
格式
类名::new

目的:
创建这个类的对象

需求:
集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中

方法引用的规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致
4.被引用方法的功能需要满足当前的需求
*/

//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
//3.封装成Student对象并收集到List集合中
//String --> Student
/* List<Student> newList = list.stream().map(new Function<String, Student>() {
@Override
public Student apply(String s) {
String[] arr = s.split(",");
String name = arr[0];
int age = Integer.parseInt(arr[1]);
return new Student(name, age);
}
}).collect(Collectors.toList());
System.out.println(newList);*/


List<Student> newList2 = list.stream().map(Student::new).collect(Collectors.toList());
System.out.println(newList2);

}
}

Student类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.itheima.a01myfunction;

public class Student {
private String name;
private int age;


public Student() {
}


public Student(String str) {
String[] arr = str.split(",");
this.name = arr[0];
this.age = Integer.parseInt(arr[1]);
}


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

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public int getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}

public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}

2.4 其他调用方式

2.4.1 使用类名引用成员方法

  • 格式:类名::成员方法
  • 范例:String::substring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Collections;

public class FunctionDemo5 {
public static void main(String[] args) {
/*
方法引用(类名引用成员方法)
格式
类名::成员方法
需求:
集合里面一些字符串,要求变成大写后进行输出


方法引用的规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
4.被引用方法的功能需要满足当前的需求

抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
在Stream流当中,第一个参数一般都表示流里面的每一个数据。
假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法

第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法

局限性:
不能引用所有类中的成员方法。
是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。

*/

//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");
//3.变成大写后进行输出
//map(String::toUpperCase)
//拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果。
list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));


//String --> String
/* list.stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
}).forEach(s -> System.out.println(s));*/







}
}

2.4.1引用数组的构造方法

  • 格式:数据类型[]::new
  • 范例:int[]::new
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class FunctionDemo6 {
public static void main(String[] args) {
/*
方法引用(数组的构造方法)
格式
数据类型[]::new
目的:
创建一个指定类型的数组
需求:
集合中存储一些整数,收集到数组当中

细节:
数组的类型,需要跟流中数据的类型保持一致。

*/

//1.创建集合并添加元素
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5);
//2.收集到数组当中

Integer[] arr2 = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(arr2));

/*Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});*/
//3.打印



}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class FunctionDemo7 {
public static void main(String[] args) {
/*
需求:
集合中存储一些字符串的数据,比如:张三,23。
收集到Student类型的数组当中
*/
//1.创建集合并添加元素
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
//2.先把字符串变成Student对象,然后再把Student对象收集起来
Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);
//打印数组
System.out.println(Arrays.toString(arr));


}
}