目录
5.1 Stream filter(Predicate predicate)——用于对流中的数据进行过滤。
5.2 Stream sorted()——对元素进行升序排序
5.4 Stream skip(long n)——跳过前几个元素
5.6 Stream distinct()——去除流中重复的元素。
5.7 static Stream concat(Stream a, Stream b)——合并a和b两个流为一个流
6.1.1 void forEach(Consumer action)——对此流运算后的元素执行遍历
6.1.2 long count()——统计此流运算后的元素个数
6.1.3 Optional min(Comparator comparator)——获取此流运算后的最小值元素
6.1.4 Optional max(Comparator comparator)——获取此流运算后的最大值元素
6.2.1 R collect(Collector collector)——把流处理后的结果收集到一个指定的集合中去
6.2.2 Object[] toArray()——把流处理后的结果收集到一个数组中去
前言
博主计划使用CSDN来记录Java后端开发的学习经历,并分享自己的编程心得和知识,期望能为那些有需求的朋友提供帮助。
博主也期望能与那些持续努力学习的朋友们共同进步!编程之路是一条充满艰辛与挑战的路,需要我们付出更多的心血,才能走得更远。只有通过不懈的研究和深入的思考,以及勤于实践,我们才能在编程的道路上取得真正的成就
一、 认识Stream流
Stream 是 Java 8 引入的一套处理集合数据的“流水线工具”,可以举一个例子,stream就像工厂流水线一样,把数据一步步过滤、转换、计算,最后得到结果。可以让你用更简洁、更高效的方式操作数组、集合等数据源。
Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好。
二、 体验stream流
现在有段代码:
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
假设有个需求是把集合中所有以“张”开头,且是3个字的元素存储到一个新的集合。
传统做法
// 找出姓张,三个字的人名到新集合。
//非Stream流方式
//定义新集合
ArrayList arrayList = new ArrayList();
//遍历list,查找匹配
for (String name : list) {
if(name.startsWith("张") && name.length() == 3){
//将符合的名字添加arrayList中
arrayList.add(name);
}
}
System.out.println(arrayList);
stream做法
//Stream流方式
List<String> arrayList2 = list.stream()
.filter(name -> name.startsWith("张") && name.length() == 3)
.collect(Collectors.toList());
System.out.println(arrayList2);
结论:
stream流可以让你用更简洁、更高效的方式操作数组、集合等数据源。
三、 stream流使用步骤
stream流的使用可以分为三个步骤,第一步是获取Stream流 Stream流代表一条流水线,并能与数据源建立连接。第二步是调用流水线的各种方法,对数据进行处理、计算。第三步获取处理的结果,遍历、统计、收集到一个新集合中返回
下面我们将开始逐步学习并使用stream
四、 获取Stream流
集合和数组分别提供了不同的API来获取stream流
4.1 集合获取stream流
集合我们知道可以分为单列集合Collection和双列集合Map,Collection作为单列集合的祖宗,它提供了default Stream<E> stream(),该方法返回当前集合对象stream流,那么对于List、Set子接口以及它们的实现类ArrayList,LinkedList、HashSet等待都能继承它的API ,所以,对于单列集合获取流的方法都是:调用stream()方法
public static void main(String[] args) {
// 目标:获取stream流。
// 1、单列集合获取流的方法都是:调用stream()方法
ArrayList<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
}
对于双列集合Map并没有直接提供获取stream流的API,但经过我们之前的学习,可以清楚的知道,我们是可以获取Map集合的Key集合、value集合,键值对集合,它们最终返回都是Set集合,所以,我们也是可以获取到stream流的
获取键的stream流,我们可以调用map集合提供的keyset()方法,该方法返回的是所有键的set集合,通过返回的set集合调用Collection提供的stream方法获取当前对象的stream流
获取值的stream流,我们可以调用map集合提供的values()方法,该方法返回的是所有值的Collection集合,然后通过返回的Collection集合调用提供的stream方法获取当前对象的stream流
获取键值对的stream流,我们可以调用map集合提供的entrySet()方法,该方法返回的是所有键值对的Set集合,然后通过返回的set集合调用Collection提供的stream方法获取当前对象的stream流
我们来编码实操一下
// 2、Map集合获取stream()流。
// 创建map集合
HashMap<String, Integer> map = new HashMap<>();
// 获取map集合的键流
Stream<String> stream1 = map.keySet().stream();
// 获取map集合的值流
Stream<Integer> stream2 = map.values().stream();
// 获取map集合的键值对对象流
Stream<Map.Entry<String, Integer>> stream3 = map.entrySet().stream();
4.2 数组获取stream流
数组获取stream流常用方法有两种,第一种是用Arrays类提供的stream方法,该方法需要传入一个数组对象,第二种方法是用Stream类提供的of方法,这个方法有很多个重载,其中有个重载是可以给它传入一个数组对象
// 3、获取数组的流。
//准备数组
String[] arr = {"张三丰", "张无忌", "赵敏", "周芷若"};
//方式1:
Stream<String> stream4 = Arrays.stream(arr);
//方式2:
Stream<String> stream5 = Stream.of(arr);
五、 Stream流提供的常用中间方法
中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)
接下来我们逐个学习,首先,我们准备个集合,然后获取个stream对象
public static void main(String[] args) {
// 目标:搞清楚Stream流提供的中间方法。
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
//获取stream流
Stream<String> stream = list.stream();
}
5.1 Stream<T> filter(Predicate<? super T> predicate)——用于对流中的数据进行过滤。
// 1、过滤:筛选数据的,查找姓“张”,并遍历
stream.filter(s->s.startsWith("张")).forEach(System.out::println);
filter方法可以对符合条件表达式数据进行筛选,比如上面查找姓“张”,说明是以张开头,我们可以利用lambda表达式,利用String提供startsWith这个方法,来判断是不是以“张”开头,最终将符合条件的数据进行筛选打印
5.2 Stream<T> sorted()——对元素进行升序排序
对于包装类型,排序默认是升序的
// 2、排序的:sorted 默认是升序。
List<Double> list2 = new ArrayList<>();
list2.add(3.14);
list2.add(1.14);
list2.add(4.14);
list2.add(2.14);
list2.add(5.14);
// 获取stream流
Stream<Double> stream2 = list2.stream();
stream2.sorted().forEach(System.out::println);
运行结果
但对于自定义类型,则需要指明排序规则,否则会抛ClassCastException异常
5.2.1 问题演示
准备Student类,并提供set、get方法,重写tostring
public class Student {
private String name;
private int age;
private double score;
private String address;
public Student() {
}
public Student(String name, int age, double score, String address) {
this.name = name;
this.age = age;
this.score = score;
this.address = address;
}
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;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
", address='" + address + '\'' +
'}' + "\n";
}
}
声明Student集合,并添加元素
// 3、集合中是对象的排序
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
获取stream流并进行排序操作
// 3、集合中是对象的排序
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
students.stream().sorted().forEach(System.out::println);
运行结果
5.2.2 解决方案
第一种方案是可以使用自定义类去实现Comparable接口,并重写提供的compareTo方法
在compareTo方法中,提供了一个类型为Student的参数o,这里代表的是传入对象
jdk官方比较规则认为:
当前对象this.getAge() - 传入对象o.getAge() 返回大正整数,代表当前对象大于传入对象
当前对象this.getAge() - 传入对象o.getAge() 返回大负整数,代表当前对象小于传入对象
当前对象this.getAge() - 传入对象o.getAge() 返回值等于0,代表当前对象等于传入对象
这里假设我们需要对学生的年龄进行升序排序,那么我们需要用当前对象的成绩减去传入对象的成绩,如果它是大于0,则可以认为是当前对象成绩的值是大于传入对象的,即this.getAge() - o.getAge()
public int compareTo(Student o) {
//jdk官方比较规则
//升序规则
// 当前对象this.getAge() - 传入对象o.getAge() 返回大正整数,代表当前对象大于传入对象
// 当前对象this.getAge() - 传入对象o.getAge() 返回大负整数,代表当前对象小于传入对象
// 当前对象this.getAge() - 传入对象o.getAge() 返回值等于0,代表当前对象等于传入对象
// if(this.getAge() - o.getAge() > 0) return 1;
// if(this.getAge() - o.getAge() < 0) return -1;
// if(this.getAge() - o.getAge() == 0) return 0;
return this.getAge() - o.getAge();
}
重写运行程序
可以发现,问题已经解决,并且按照我们预期,对年龄进行了升序排序
除了上述继承Comparable接口的方式,还有另一种方式,就是可以利用softed方法的另一个重载,这个重载是支持我们可以去自定义它的排序规则
Comparator是什么呢?我们按照ctrl加鼠标点击,进入看看
可以看到 Comparator其实就是一个函数式接口,里面有一个compare函数,需要我们待会去实现它,来完成自定义排序规则,它既然是函数式接口,我们可以考虑用匿名内部类的方式来实现它,也可以使用Lambda表达式进行优化,我们两种方式都体验一下
实现方式一:匿名内部类方式
返回我们的Test类,采用匿名内部类方式来重写compare方法
这个compare方法 需要两个参数,一个是类似于compareTo方法中的当前对象,第二个参数是传入对象,但是这里不是这么称呼,而是直接比较它们两个参数的值来决定如何排序
如果认为第一个元素 > 第二个元素 返回正整数即可。
如果认为第一个元素 < 第二个元素返回负整数即可。
如果认为第一个元素 = 第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复
这里我们按照学生的成绩进行升序排序处理,因为学生成绩是double类型,我们可以使用Double包装类提供的compare方法进行比较值,这个方法也需要传入两个值,如果前者大于后者,则会返回大于1的数,如果两个值相等,则会返回0,否则就返回-1
public static void main(String[] args) {
// 3、集合中是对象的排序
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
students.stream().sorted(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getScore(), o2.getScore());
}
}).forEach(System.out::println);
}
运行结果
可以看到,TreeSet集合已经按照我们的预期,实现了按学生成绩进行升序排序
实现方式二:lambda表达式优化
把鼠标放到Comparator那里,按住ALT+ENTER(回车),将表达式替换成lambda表达式即可
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
students.stream().sorted((o1, o2) -> Double.compare(o1.getScore(), o2.getScore())).forEach(System.out::println);
}
运行结果
可以看到,两种方式效果是一样的
5.3 Stream<T> limit——获取前几个元素
// 目标:搞清楚Stream流提供的中间方法。
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
// 获取stream流
Stream<String> stream = list.stream();
// 4、limit 取前几个
Stream<String> limit = stream.limit(3);
limit.forEach(System.out::println);
运行结果
可以用limit来获取集合或数据里面前几个元素
5.4 Stream<T> skip(long n)——跳过前几个元素
// 目标:搞清楚Stream流提供的中间方法。
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
// 获取stream流
Stream<String> stream3 = list.stream();
Stream<String> skip = stream3.skip(3);
skip.forEach(System.out::println);
运行结果
5.5 加工方法map——对元素进行加工,并返回对应的新流
可以使用stream流提供的map方法,对流中的数据进行加工,map方法的入参是一个Function接口类型,Function是一个函数式接口
使用时可以用lambda进行优化,也可以用匿名内部类的方式去实现apply方法,这个方法入参是个泛型,可以在这个方法里面写我们需要的逻辑,比如我们可以获取集合里面的年龄属性,那么我们可以这样写
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
// 6、加工方法:map
students.stream().sorted().map(new Function<Student, Object>() {
@Override
public Object apply(Student student) {
return student.getAge();
}
}).forEach(s-> System.out.println(s));
运行结果
也可以用lambda进行优化,鼠标放到Function,按照Alt + 回车,生成lambda表达式
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
students.stream().sorted().map(s-> "姓名:"+s.getName()+","+"年龄:"+s.getAge()).forEach(s-> System.out.println(s));
运行结果
注意,map加工后返回元素类型可以与原集合元素类型不一样,返回元素的类型由map里面决定
5.6 Stream<T> distinct()——去除流中重复的元素。
在stream流中,可以使用distinct方法对流中的元素进行去重,它的底层其实就是调用了Object提供的HashCode方法来获取哈希值和调用Object提供equals方法,来比较这个对象是否是同一个对象
5.6.1 基本包装类型去重
我们都知道,如果只是调用Object类提供的equals方法,那么这个方法比较的就是内存地址,对于不同的对象,这个内存地址是不一样的,但是对于基本包装类型(如Integer、String、Double),它们底层都有重写了Object提供的equals方法和HashCode方法,因此,它们比较的不在是内存地址,而是具体的值,所以基本包装类型可以直接用distinct去重
准备数据
List<Double> list2 = new ArrayList<>();
list2.add(3.14);
list2.add(1.14);
list2.add(4.14);
list2.add(4.14);
list2.add(2.14);
list2.add(5.14);
list2.add(5.14);
// 8、去重
list2.stream().distinct().forEach(System.out::println);
运行结果
可以发现,对于基本包装类型, distinct已经帮我们去除重复元素5.14和4.14
5.6.2 自定义类型去重
刚刚我们了解到去重的原理是基于比较equals和比较元素的哈希值进行去重的,所以,我们自定义类型必须要重写equals方法和hashCode方法,否则不能去重
准备数据
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
students.add(new Student("小花", 21, 60.0, "深圳"));
// 8、去重
students.stream().distinct().forEach(System.out::println);
运行结果
可以发现,对于基本数据类型,我们没有重写 equals方法和hashCode方法,distinct是不能帮我们去重的
解决方案:重写equals方法和hashCode方法
返回我们的Student类,按住ALT + Insert,选择生成equals和hashCode,然后一直确定即可
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Double.compare(score, student.score) == 0 && Objects.equals(name, student.name) && Objects.equals(address, student.address);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score, address);
}
第九步:重写equals源码分析
第十步: 重写HashCode源码分析
第十一步:重新运行程序
可以发现,HashSet集合已经帮我们去除了相同的元素数据
5.7 static <T> Stream<T> concat(Stream a, Stream b)——合并a和b两个流为一个流
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
List<Double> list2 = new ArrayList<>();
list2.add(3.14);
list2.add(1.14);
list2.add(4.14);
list2.add(4.14);
list2.add(2.14);
list2.add(5.14);
list2.add(5.14);
Stream<String> stream1 = list.stream();
Stream<Double> stream2 = list2.stream();
Stream<Object> stream3 = Stream.concat(stream1, stream2);
stream3.forEach(System.out::println);
System.out.println("----------------------------");
运行结果
可以用concat来讲两个流中的元素进行合并,并返回一个新的流
六、 终结方法、收集Stream流
终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
6.1 Stream流的终结方法 
6.1.1 void forEach(Consumer action)——对此流运算后的元素执行遍历
forEach其实在之前的例子里面已经用过了,它就是其中一种终结方法,并且可以遍历输出元素
// 目标:学习Stream流的终结方法。
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
// 1、遍历方法
list.stream().filter(name->name.startsWith("张")).forEach(System.out::println);
运行结果
6.1.2 long count()——统计此流运算后的元素个数
// 目标:学习Stream流的终结方法。
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
// 2、统计数据的个数 count()
long count = list.stream().filter(name -> name.startsWith("张")).count();
System.out.println("姓张的同学人数:"+count);
运行结果
6.1.3 Optional<T> min(Comparator<? super T> comparator)——获取此流运算后的最小值元素
这个方法需要有一个入参,就是Comparator类型的参数,上面我们已经知道,Comparator是一个接口,需要我们重写它的比较方法compare方法才可以,这里我们直接lambda进行编写
// 3、挑选出最小值
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
Optional<Student> min = students.stream().min((s1, s2) -> s1.getAge() - s2.getAge());
Student student = min.get();
System.out.println("年龄最小是:"+student.getAge()+"岁,姓名:"+student.getName());
}
运行结果
为什么返回的是Optional类型?这是因为可能没有最小年龄同学会返回null容易发生空指针异常,所以返回Optional<Student>接收null的值,所以我们可以对这个代码进行优化
// 3、挑选出最小值
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
Optional<Student> min = students.stream().min((s1, s2) -> s1.getAge() - s2.getAge());
//为什么返回Optional<Student>,而不是最小年龄的学生Student? 答:因为可能没有最小年龄同学会返回null容易发生空指针异常,所以返回Optional<Student>接收null的值
if(!min.isEmpty()){
Student student = min.get();
System.out.println("年龄最小是:"+student.getAge()+"岁,姓名:"+student.getName());
}
6.1.4 Optional<T> max(Comparator<? super T> comparator)——获取此流运算后的最大值元素
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
// 4、挑选出最大值
Optional<Student> max = students.stream().max((s1, s2) -> s1.getAge() - s2.getAge());
if(max!=null){
Student student = max.get();
System.out.println("年龄最大是:"+student.getAge()+"岁,姓名:"+student.getName());
}
运行结果
经过上面的讲解,我们已经清除的知道Stream流的终结方法和使用方法,但是,有个非常要注意的一点是,一旦用了总结方法,那么这个流的对象就被关闭,不能再去使用中间方法,需要重新获取Stream流
6.2 收集Stream流
收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。
其实收集Stream流也是总结方法中的一种方式,Stream提供了两个常用的收集流的方法
6.2.1 R collect(Collector collector)——把流处理后的结果收集到一个指定的集合中去
方式一:收集到一个全新的List集合
// 目标:收集Stream流。把流收集成集合: 流只能用一次
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
// 收集List集合
List<String> list2 = list.stream().filter(name -> name.startsWith("张")).collect(Collectors.toList());
System.out.println("新的List:" + list2);
运行结果
方式二:收集到一个权限的Set集合
// 目标:收集Stream流。把流收集成集合: 流只能用一次
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
// 收集成Set集合
Set<String> set = list.stream().collect(Collectors.toSet());
System.out.println("新的Set:" + set);
运行结果
方式三:收集到一个权限的Map集合
收集到Map集合和前两个不太一样,因为它是双列集合,所以在收集的时候,必须指定Key和Value值,这两个参数类型都是Function接口类型,经过上面的学习,我们已经知道,它也是函数式接口,需要我们重写apply方法,来写我们的逻辑
准备数据
// 收集成Map集合。
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 18, 90.0, "北京"));
students.add(new Student("小红", 19, 80.0, "上海"));
students.add(new Student("小刚", 20, 70.0, "广州"));
students.add(new Student("小花", 21, 60.0, "深圳"));
方式一:匿名内部类方式
// 方式1:匿名内部类方式
Map<String, Integer> map1 = students.stream().filter(s -> s.getName().startsWith("小")).collect(Collectors.toMap(new Function<Student, String>() {
@Override
public String apply(Student student) {
return student.getName();
}
}, new Function<Student, Integer>() {
@Override
public Integer apply(Student student) {
return student.getAge();
}
}));
System.out.println("map1:"+map1);
这里是先用了filter方法对流中的元素进行筛选,筛选出以“小”开头的元素,然后再用收集流的方法,以学生的名字作为Key键,年龄作为Value值
运行结果
方式二:lambda方式
鼠标放到Function那里,按照alt+回车即可替换成lambda表达式
// 方式2:lambda方式
Map<String, Integer> map2 = students.stream().filter(s -> s.getName().startsWith("小")).collect(
Collectors.toMap(student -> student.getName(), student -> student.getAge()));
System.out.println("map2:"+map2);
运行结果
方式三(推荐):特殊方法引用方式(类名::实例方法,要求:传入的第一个参数是方法体内主调用方法参数,其他参数作为调用方法的参数)
把鼠标放到student.getName()或student.getAge()按住alt + 回车,即可替换成方法引用方式
// 方式3(推荐):特殊方法引用方式(类名::实例方法,要求:传入的第一个参数是方法体内主调用方法参数,其他参数作为调用方法的参数)
Map<String, Integer> map3 = students.stream().filter(s -> s.getName().startsWith("小")).collect(
Collectors.toMap(Student::getName, Student::getAge));
System.out.println("map3:"+map3);
运行结果
6.2.2 Object[] toArray()——把流处理后的结果收集到一个数组中去
可以用toArray将Stream流转成数组
// 目标:收集Stream流。把流收集成集合: 流只能用一次
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张无忌");
Object[] array = list.stream().toArray();
System.out.println("数组:" + array);
运行结果
默认打印的是内存地址,我们需要用Ayyays类提供的ToString方法,将内存地址转成字符串
System.out.println("数组:" + Arrays.toString(array));