Java&Lambda表达式、Stream流

【Lambda表达式、Stream流】

Lambda表达式

讲解

Lambda表达式概述

它是一个JDK8开始一个新语法。它是一种“代替语法”——可以代替我们之前编写的“面向某种接口”编程的情况。

public class MyRun implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("i = " + i);
        }
	}
}
public class Demo {
    public static void main(String[] args) {
        //1.使用子类
    //    new Thread(new MyRun()).start();

        //2.使用匿名内部类
        /*new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("i = " + i);

                }
            }
        }).start();*/

        //3.使用Lambda表达式
        new Thread(()-> {
            for (int i = 0; i < 100; i++) {
                System.out.println("i = " + i);
            }
        }).start();
            
        for (int k = 0; k < 100; k++) {
            System.out.println("k = " + k);
        }
    }
}
函数式编程思想
  • “面向对象”的编程思想:必须依靠对象,通过对象调用方法来完成功能

​ 例如:在调用Thread()的构造方法,需要:1).先定义Runnable实现类;2).创建实现类对象;3).传入实现类对象;

  • 函数式编程思想:

    例如:在调用Thread()的构造方法,不需要定义实现类;不需要创建子类对象;只需要传入一个“方法”即可。

    “方法”: Lambda表达式

  • 所以“函数式编程思想”在写法上要比较简洁。

  • 大白话: Lambda表达式其实就是替换我们以前的接口对象

  • **使用Lambda表达式的前提:**使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的Runnable、 Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。

  • 注意: 有且仅有一个抽象方法的接口,称为“函数式接口”。函数式接口可以使用@FunctionalInterface注解标识

Lambda的标准格式
  • Lambda表达式的标准格式:

    (参数类型 参数名)->{代码语句}
  • 格式说明:

    • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
    • ->是新引入的语法格式,代表指向动作。
    • 大括号内的语法与传统方法体要求基本一致。
  • 案例1:接口中的方法是“无参、无返回值”的:

    new Thread(()->{
        for (int i = 0; i < 100; i++) {
            System.out.println("i = " + i);
        }
    }).start();
    
  • 案例2:接口中的方法是“有参、有返回值”的:

     	   ArrayList<String> list = new ArrayList<>();
            list.add("jfdksl");
            list.add("jfdskljfs");
            list.add("afds");
            list.add("jfdowjieoj");
            
            Collections.sort(list,(String s1,String s2) -> {//直接重写compare()方法
                return s1.length() - s2.length();//由于compare()方法声明了返回一个int值,所以这里重写时必须返回一个int值。
            });
Lambda的省略格式
  • 小括号内参数的类型可以省略;

  • 如果小括号内有且仅有一个参数,则小括号和参数类型可以一起省略;

  • 如果大括号内有且仅有一个语句,则可以同时省略:一对大括号,语句后的分号,return关键字;

  • 案例:

    public interface IAnimal{
        public void eat(String s);
    }
    
    public class Cat implements IAnimal{
        @Override
        public void eat(String s) {
            System.out.println("猫吃:" + s);
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            //1.使用子类
            fun(new Cat());
            
            //2.使用匿名内部类
            fun(new IAnimal() {
                @Override
                public void eat(String s) {
                    System.out.println("狗吃:" + s);
                }
            });
            
            //3.使用Lambda--标准格式
            fun((String s) -> {
                System.out.println("猪吃:" + s);
            });
            
            //4.使用Lambda--省略格式
            fun( s -> System.out.println("猪吃:" + s));
        }
    
        public static void fun(IAnimal animal) {//IAnimal animal = new Cat();
            animal.eat("红烧肉");
        }
    }
Lambda的几种使用形式
  • 用变量的形式:

    Runnable run = () -> {System.out.println("线程启动…");};
  • 在调用方法时,作为“实参” :

    new Thread(()->{System.out.println("线程启动…");}).start();
  • 作为方法的“返回值”:

    public Runnable get(){
    	return ()-> { System.out.println("线程启动…");};
    }

4.小结

面向对象和函数式编程:
     面向对象: 必须依靠对象,使用对象调用方法来完成功能
     函数式编程:不需要依靠对象,直接传入功能来执行即可(传入Lambda表达式)
     Lambda表达式就是用来替换函数式接口的对象
     
 Lambda的标准格式:
      (数据类型 参数名)->{ 需要执行的代码 }

      格式说明:
             - 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
             - ->是新引入的语法格式,代表指向动作。
             - 大括号内的语法与传统方法体要求基本一致。

     补充:
        1.小括号中的内容和函数式接口中抽象方法的小括号内容一致,参数名可以不一致,参数类型,参数个数,参数顺序必须一致
       2.大括号中的内容和以前实现函数式接口抽象方法的方法体一致
       3.Lambda表达式大括号中的代码是调用了该函数式接口的抽象方法才会执行

          使用Lambda表达式的前提: 接口为函数式接口,也就是接口中有且仅有一个抽象方法
          常见的函数式接口: Runnable  Comparable Comparator Callable ....
 Lambda的省略格式:
         - 小括号内参数的类型可以省略;
         - 如果小括号内有且仅有一个参数,则小括号和参数类型可以一起省略;
         - 如果大括号内有且仅有一条语句,则可以同时省略:一对大括号,语句后的分号,return关键字;
 Lambda的几种使用形式: 使用场景
                1.变量的形式:变量的类型为函数式接口类型,那么可以赋值一个Lambda表达式
                2.参数的形式:方法的形参类型为函数式接口类型,那么就可以传入一个Lambda表达式
                3.返回值的形式:方法的返回值类型为函数式接口类型,那么就可以返回一个Lambda表达式

Stream

知识点-- Stream

1引言

从JDK8开始,Java开始支持Lambda,而Java类库中为了支持Lambda,而制作了一些“应用”——Stream就是一个典型的应用。

Stream流的概念:它是一个“接口”,它的功能类似于“迭代器Iterator”,也是对大量的数据进行遍历的,但它比迭代器更强大,可以对大量的数据进行:过滤、筛选、汇总等等操作。

试想一下,如果希望对集合中的元素进行筛选过滤:

  1. 将集合A根据条件一过滤为子集B
  2. 然后再根据条件二过滤为子集C

例如: 有一个List集合,要求:

​ 1.将List集合中姓张的的元素过滤到一个新的集合中

​ 2.然后将过滤出来的姓张的元素中过滤出长度为3的元素,存储到一个新的集合中

传统方式:

public class Demo02NormalFilter {
  	public static void main(String[] args) {
      	List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("张")) {
              	zhangList.add(name);
            }
        }

        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
              	shortList.add(name);
            }
        }

        for (String name : shortList) {
          	System.out.println(name);
        }
    }
}

Stream的更优写法

public class Demo03StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        list.stream()
          	.filter(s -> s.startsWith("张"))
            .filter(s -> s.length() == 3)
            .forEach(System.out::println);
    }
}

直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。代码中并没有体现使用线性循环或是其他任何算法进行遍历,我们真正要做的事情内容被更好地体现在代码中。

2流式思想概述

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

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

这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。而最右侧的数字3是最终结果。

这里的filtermapskip都是在对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法count执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。

Stream流:类似于“流水线”,每次操作流,都可以将“结果”发送给下一个操作。

Stream流的特点:

  • Stream流是“一次性”的,只要调用这个流的一个方法,方法执行后,这个流立即失效。但此方法,会将工作结果存储到一个“新流”中,可以继续对这个“新流”继续操作,Stream流是不能重复使用的
  • Stream流不会存储数据
  • Stream流不会修改数据源
  • Stream流搭建的函数模型里面一定要有终结方法,并且只有有了终结方法,前面的延迟方法才会执行
    • 终结方法: Stream流中方法的返回值类型不是Stream
    • 延迟方法: Stream流中方法的返回值类型是Stream
3获取流方式

java.util.stream.Stream<T>是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)

获取一个流非常简单,有以下几种常用的方式:

  • 所有的Collection集合都可以通过stream默认方法获取流;
  • Stream接口的静态方法of可以获取数组对应的流。

方式1 : 根据Collection获取流

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

import java.util.*;
import java.util.stream.Stream;

public class Demo04GetStream {
    public static void main(String[] args) {
        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();
    }
}

方式2 : 根据Map获取流

java.util.Map接口不是Collection的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Demo05GetStream {
    public static void main(String[] args) {
        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();
    }
}

方式3 : 根据数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of,使用很简单:

import java.util.stream.Stream;

public class Demo06GetStream {
    public static void main(String[] args) {
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream<String> stream = Stream.of(array);
    }
}

备注:of方法的参数其实是一个可变参数,所以支持数组。

4常用方法

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

  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。本小节中,终结方法包括countforEach方法。
  • 非终结方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法。)

函数拼接与终结方法

在上述介绍的各种方法中,凡是返回值仍然为Stream接口的为函数拼接方法,它们支持链式调用;而返回值不再为Stream接口的为终结方法,不再支持链式调用。如下表所示:

方法名方法作用方法种类是否支持链式调用
count统计个数终结
forEach逐一处理终结
filter过滤函数拼接
limit取用前几个函数拼接
skip跳过前几个函数拼接
map映射函数拼接
concat组合函数拼接

备注:本小节之外的更多方法,请自行参考API文档。

forEach : 逐一处理

虽然方法名字叫forEach,但是与for循环中的“for-each”昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的

void forEach(Consumer<? super T> action);

该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。例如:

import java.util.stream.Stream;

public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
        stream.forEach(s->System.out.println(s));
    }
}
count:统计个数

正如旧集合Collection当中的size方法一样,流提供count方法来数一数其中的元素个数:

long count();

该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。基本使用:

public class Demo09StreamCount {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.filter(s -> s.startsWith("张"));
        System.out.println(result.count()); // 2
    }
}
filter:过滤

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

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

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

基本使用

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

public class Demo07StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.filter(s -> s.startsWith("张"));
    }
}

在这里通过Lambda表达式来指定了筛选的条件:必须姓张。

limit:取用前几个

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

Stream<T> limit(long maxSize);

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

import java.util.stream.Stream;

public class Demo10StreamLimit {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.limit(2);
        System.out.println(result.count()); // 2
    }
}
skip:跳过前几个

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

Stream<T> skip(long n);

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

import java.util.stream.Stream;

public class Demo11StreamSkip {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.skip(2);
        System.out.println(result.count()); // 1
    }
}
map:映射

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

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

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

基本使用

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

import java.util.stream.Stream;

public class Demo08StreamMap {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("10", "12", "18");
        Stream<Integer> result = original.map(s->Integer.parseInt(s));
    }
}

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

concat:组合

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

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

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

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

import java.util.stream.Stream;

public class Demo12StreamConcat {
    public static void main(String[] args) {
        Stream<String> streamA = Stream.of("张无忌");
        Stream<String> streamB = Stream.of("张翠山");
        Stream<String> result = Stream.concat(streamA, streamB);
    }
}
5 Stream综合案例

现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:

  1. 第一个队伍只要名字为3个字的成员姓名;
  2. 第一个队伍筛选之后只要前3个人;
  3. 第二个队伍只要姓张的成员姓名;
  4. 第二个队伍筛选之后不要前2个人;
  5. 将两个队伍合并为一个队伍;
  6. 根据姓名创建Person对象;
  7. 打印整个队伍的Person对象信息。

两个队伍(集合)的代码如下:

public class DemoArrayListNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");

        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
		// ....
    }
}

Person类的代码为:

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;
    }
}

传统方式

使用for循环 , 示例代码:

public class DemoArrayListNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        // ...

        List<String> two = new ArrayList<>();
        // ...

        // 第一个队伍只要名字为3个字的成员姓名;
        List<String> oneA = new ArrayList<>();
        for (String name : one) {
            if (name.length() == 3) {
                oneA.add(name);
            }
        }

        // 第一个队伍筛选之后只要前3个人;
        List<String> oneB = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            oneB.add(oneA.get(i));
        }

        // 第二个队伍只要姓张的成员姓名;
        List<String> twoA = new ArrayList<>();
        for (String name : two) {
            if (name.startsWith("张")) {
                twoA.add(name);
            }
        }

        // 第二个队伍筛选之后不要前2个人;
        List<String> twoB = new ArrayList<>();
        for (int i = 2; i < twoA.size(); i++) {
            twoB.add(twoA.get(i));
        }

        // 将两个队伍合并为一个队伍;
        List<String> totalNames = new ArrayList<>();
        totalNames.addAll(oneB);
        totalNames.addAll(twoB);

        // 根据姓名创建Person对象;
        List<Person> totalPersonList = new ArrayList<>();
        for (String name : totalNames) {
            totalPersonList.add(new Person(name));
        }

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

运行结果为:

Person{name='宋远桥'}
Person{name='苏星河'}
Person{name='洪七公'}
Person{name='张二狗'}
Person{name='张天爱'}
Person{name='张三'}

Stream方式

等效的Stream流式处理代码为:

public class DemoStreamNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        // ...

        List<String> two = new ArrayList<>();
        // ...

        // 第一个队伍只要名字为3个字的成员姓名;
        // 第一个队伍筛选之后只要前3个人;
        Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);

        // 第二个队伍只要姓张的成员姓名;
        // 第二个队伍筛选之后不要前2个人;
        Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);

        // 将两个队伍合并为一个队伍;
        // 根据姓名创建Person对象;
        // 打印整个队伍的Person对象信息。
        Stream.concat(streamOne, streamTwo).map(s-> new Person(s)).forEach(s->System.out.println(s));
    }
}

运行效果完全一样:

Person{name='宋远桥'}
Person{name='苏星河'}
Person{name='洪七公'}
Person{name='张二狗'}
Person{name='张天爱'}
Person{name='张三'}
6 收集Stream结果

对流操作完成之后,如果需要将其结果进行收集,例如获取对应的集合、数组等,如何操作?

收集到集合中

Stream流提供collect方法,其参数需要一个java.util.stream.Collector<T,A, R>接口对象来指定收集到哪种集合中。幸运的是,java.util.stream.Collectors类提供一些方法,可以作为Collector接口的实例:

  • public static <T> Collector<T, ?, List<T>> toList():转换为List集合。
  • public static <T> Collector<T, ?, Set<T>> toSet():转换为Set集合。

下面是这两个方法的基本使用代码:

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo15StreamCollect {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        List<String> list = stream.collect(Collectors.toList());
        Set<String> set = stream.collect(Collectors.toSet());
    }
}
收集到数组中

Stream提供toArray方法来将结果放到一个数组中,由于泛型擦除的原因,返回值类型是Object[]的:

Object[] toArray();

其使用场景如:

import java.util.stream.Stream;

public class Demo16StreamArray {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        Object[] objArray = stream.toArray();
    }
}

4.小结

获取Stream流方式:
1.Collection接口中有一个stream()方法,可以获取流
	default Stream<E> stream():获取一个Stream流
    1.通过List集合获取:
    2.通过Set集合获取

    通过Map集合获取:
        3.1 使用所有键的集合来获取流
        3.2 使用所有值的集合来获取流
        3.3 使用所有键值对的集合来获取流

 2.Stream流中有一个static <T> Stream<T> of(T... values)
        4.通过数组获取:
        5.通过直接给多个数据的方式
        
Stream流的特点:
    1.首先应该搭建完整的函数模型,函数模型中一定要包含终结方法
           终结方法: Stream流中的方法的返回值类型不是Stream类型
           延迟方法:Stream流中的方法的返回值类型是Stream类型
    2.Stream流不能存储数据
    3.Stream流不能重复使用
    4.Stream流不会修改数据源

Stream常用方法:
     终结方法:
        void forEach(Consumer<? super T> action)  对此流的每个元素执行操作。 该方法并不保证元素的逐一消费动作在流中是被有序执行的。
          参数Consumer<T>:是一个接口,并且是一个函数式接口,所以参数可以传入该接口对应的Lambda表达式
           Consumer<T>接口中的抽象方法:  void accept(T t);

         long count() 返回此流中的元素个数。

延迟方法:
 Stream<T> filter(Predicate<? super T> predicate): 过滤功能的方法,把符合条件的元素组成一个新的流返回
             参数Predicate<T>:是一个接口,并且是一个函数式接口,所以参数可以传入该接口对应的Lambda表达式
             Predicate<T>接口的抽象方法: boolean test(T t)

  Stream<T> limit(long maxSize) : 保留流中的前几个元素,组成一个新的流返回
  Stream<T> skip(long n) : 跳过前几个元素,剩余的元素组成一个新的流返回
  <R> Stream<R> map(Function<? super T,? extends R>  mapper) : 映射 把流中元素的T类型转换为R类型,组成一个新的流并返回
      	    T和R的类型可以一致,也可以不一致
             数Function<T,R>:是一个接口,并且是一个函数式接口,所以参数可以传入该接口对应的Lambda表达式
             Function<T,R>接口的抽象方法: R apply(T t);
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b): a和b流合并,组成一个新的流返回
  Stream流的使用步骤: 获取流--->操作流--->处理结果
  
收集Stream结果:
     收集到集合: List集合,Set集合
     Stream流中提供了一个方法,可以把流中的数据收集到单列集合中
     <R,A> R collect(Collector<? super T,A,R> collector): 把流中的数据收集到单列集合中
                 参数Collector<? super T,A,R>: 决定把流中的元素收集到哪个集合中
                 返回值类型是R,也就是说R指定为什么类型,就是收集到什么类型的集合
                  参数Collector如何得到? 使用java.util.stream.Collectors工具类中的静态方法:
                - public static <T> Collector<T, ?, List<T>> toList():转换为List集合。
                - public static <T> Collector<T, ?, Set<T>> toSet():转换为Set集合。

收集数组: 流中元素对应类型的数组
    Stream流中提供了一个方法,可以把流中的数据收集到数组中
        Object[] toArray()  默认收集到Object数组中
        <A> A[] toArray(IntFunction<A[]> generator)  收集到指定类型的数组  了解  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值