一、定时器

* 定时器Timer类:在java.util包下!线程调度任务以供将来在后台线程中执行的功能。 任务可以安排一次执行,或者定期重复执行。* 构造方法:Timer() 创建一个新的计时器。* 功能方法:void schedule(TimerTask task, Date firstTime, long period) 从指定的时间开始,对指定的任务执行重复的 固定延迟执行 。参数1:定时器要执行的任务功能!需要传入一个TimerTask类型的对象!(TimerTask它是一个抽象类:里面有一个抽象方法!run​())其实需要的是TimerTask的一个子类对象(子类是需要重写抽象方法run) ====>>> 匿名内部类参数2:延迟的时间 [毫秒值为单位]参数3:周期(每间隔多久重复执行一次task任务)[毫秒值为单位]
public class Test {public static void main(String[] args) {System.out.println(new Date().toLocaleString());// 创建定时器核心对象Timer timer = new Timer();// 调用方法timer.schedule(new TimerTask() {// 任务方法@Overridepublic void run() {System.out.println("祝您生日快乐!" + new Date().toLocaleString());// 发送一封邮件!【查询数据库,获得所有用户里面满足是明天是生日的寿星,发送邮件】 2000-11-08}},3000,2000);  // 第二个参数(使用方法,获得启动程序后到今天晚上12:00时间差<毫秒值>),第三个参数:1天时间对应的毫秒值!}}
  • 系统默认当Timer运行结束后,如果没有手动终止,那么则只有当系统的垃圾收集被调用的时候才会对其进行回收终止。既然这样,我们可以使用System.gc()来实现程序的手动终止:
import java.util.Timer;
import java.util.TimerTask;class Task extends TimerTask{@Overridepublic void run() {System.out.println("******程序执行******");System.gc();}
}public class TaskTest {public static void main(String[] args){Timer timer = new Timer();Task task = new Task() ;timer.schedule(task, 3000);    //这里的单位是毫秒}
}

运行一下,OK,程序运行结束的同时,也成功终止。

但是Sytem.gc()在一个项目中是不能随便调用的,我们做做小测试如此做无可厚非,但是在项目中如此写,太不合实际了。

* 那么我们可以考虑用Timer类自带的cancel()方法,实现Timer的终止。

来看一下API中对cancel()方法的描述:

public void cancel()
Terminates this timer(终结这个timer), discarding any currently scheduled tasks(抛弃所有当前正在执行的TimerTask). Does not interfere with a currently executing task (if it exists). Once a timer has been terminated, its execution thread terminates gracefully, and no more tasks may be scheduled on it.
Note that calling this method from within the run method of a timer task that was invoked by this timer absolutely guarantees that the ongoing task execution is the last task execution that will ever be performed by this timer.This method may be called repeatedly; the second and subsequent calls have no effect.

那么我们来实现一下:

import java.util.Timer;
import java.util.TimerTask;public class TaskTest {public static void main(String[] args) {Timer timer = new Timer();// 三秒后开始执行,每隔一秒执行一次timer.schedule(new Task(timer), 3 * 1000, 1000);}
}class Task extends TimerTask {private Timer timer;public Task(Timer timer) {this.timer = timer;}int i = 1;@Overridepublic void run() {System.out.println("******程序执行******");//当执行到第5秒,程序结束if (i++ == 5) {this.timer.cancel();System.out.println("#### 程序结束 ####");}}
}

OK,成功结束程序。

二、Lambda表达式

2.1 函数式编程思想概述

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。

做什么,而不是怎么做

我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:将run方法体内的代码传递给Thread类知晓。

传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那,有没有更加简单的办法?如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。

2.2 Lambda的优化

当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程

2.2.1 传统写法

public class MyThread01 {public static void main(String[] args) {// 开启新线程并执行new Thread(new Runnable() {@Overridepublic void run() {System.out.println("多线程任务执行!");}}).start();}}

本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动。

代码分析:

* Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
* 为了指定run的方法体,不得不需要Runnable接口的实现类;
* 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
* 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
* 而实际上,似乎只有方法体才是关键所在。

2.2.2 Lambda表达式写法

借助Java8的全新语法,上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

public class MyThread02 {public static void main(String[] args) {// 开启新线程并执行new Thread(()->System.out.println("多线程任务执行!")).start();}}

这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。

不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!

2.3 Lambda的格式

2.3.1 标准格式

Lambda省去面向对象的条条框框,格式由3个部分组成:

* 一些参数
* 一个箭头
* 一段代码

Lambda表达式的标准格式为:

* 格式:(参数类型 参数名称) -> { 代码语句 }
* 解释:小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。->是新引入的语法格式,代表指向动作。大括号内的语法与传统方法体要求基本一致。

匿名内部类与lambda对比:

// 开启新线程并执行
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("多线程任务执行!");}
}).start();

仔细分析该代码中,Runnable接口只有一个run方法的定义:

public abstract void run();

即制定了一种做事情的方案(其实就是一个方法):

* 无参数:不需要任何条件即可执行该方案。
* 无返回值:该方案不产生任何结果。
* 代码块(方法体):该方案的具体执行步骤。

同样的语义体现在Lambda语法中,要更加简单:

()->System.out.println("多线程任务执行!")
* 前面的一对小括号即run方法的参数(无),代表不需要任何条件
* 中间的一个箭头代表将前面的参数传递给后面的代码;
* 后面的输出语句即业务逻辑代码。

参数和返回值

下面举例演示java.util.Comparator接口的使用场景代码,其中的抽象方法定义为:

public abstract int compare(T o1, T o2);

当需要对一个对象数组进行排序时,Arrays.sort方法需要一个Comparator接口实例来指定排序的规则。假设有一个Person类,含有String name和int age两个成员变量:

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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

1. 如果使用传统的代码对Person[]数组进行排序,写法如下:

public class PersonComparator {public static void main(String[] args) {// 准备数组数据Person[] persons = {new Person("jack", 19), new Person("rose", 18), new Person("tom", 23)};// 匿名内部类设定比较规则Comparator<Person> comparator = new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge()-o2.getAge();}};// 数组工具类方法排序Arrays.sort(persons,comparator);// 遍历打印for (Person person : persons) {System.out.println(person);}}

这种做法在面向对象的思想中,似乎也是“理所当然”的。其中Comparator接口的实例(使用了匿名内部类)代表了“按照年龄从小到大”的排序规则。

代码分析

下面我们来搞清楚上述代码真正要做什么事情。

* 为了排序,Arrays.sort方法需要排序规则,即Comparator接口的实例,抽象方法compare是关键;
* 为了指定compare的方法体,不得不需要Comparator接口的实现类;
* 为了省去定义一个ComparatorImpl实现类的麻烦,不得不使用匿名内部类;
* 必须覆盖重写抽象compare方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
* 实际上,只有参数和方法体才是关键。

2. Lambda写法

public class PersonComparatorLambda {public static void main(String[] args) {// 准备数组数据Person[] persons = {new Person("jack", 19), new Person("rose", 18), new Person("tom", 23)};// 匿名内部类设定比较规则/*Comparator<Person> comparator = new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge()-o2.getAge();}};*/// 数组工具类方法排序 (lambda简化书写)Arrays.sort(persons,(Person p1,Person p2)->{return p1.getAge()-p2.getAge();});// 遍历打印for (Person person : persons) {System.out.println(person);}}}

2.3.2 省略格式

在Lambda标准格式的基础上,使用省略写法的规则为:

1. 小括号内参数的类型可以省略;
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。【后面3个是一起省略!】

掌握这些省略规则后,请对应地回顾本章开头的多线程案例。

Lambda强调的是“做什么”而不是“怎么做”,所以凡是可以根据上下文推导得知的信息,都可以省略。例如上例还可以使用Lambda的省略写法:

* Runnable接口简化:1. () -> System.out.println("多线程任务执行!")
* Comparator接口简化:2. Arrays.sort(array, (a, b) -> a.getAge() - b.getAge());

2.4 Lambda的前提条件

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
2. 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

有且仅有一个抽象方法的接口,称为“函数式接口”。

三、Stream流

在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端

3.1 Stream流引入

传统集合的多步遍历代码

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

public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("卡特琳娜");list.add("卡萨丁");list.add("维迦");list.add("卡兹克");list.add("拉莫斯");for (String name : list) {System.out.println(name);}
}

这是一段非常简单的集合遍历操作:对集合中的每一个字符串都进行打印输出操作。

循环遍历的弊端

Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行了对比说明。现在,我们仔细体会一下上例代码,可以发现:

  • for循环的语法就是"怎么做"
  • for循环的循环体才是"做什么"

为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环。前者是目的,后者是方式。

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

1. 将集合A根据条件一过滤为子集B;(筛选所有姓卡的人)
2. 然后再根据条件二过滤为子集C。(筛选名字有三个字的人)

那怎么办?在Java 8之前的做法可能为:

public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("卡特琳娜");list.add("卡萨丁");list.add("维迦");list.add("卡兹克");list.add("拉莫斯");List<String> kaList = new ArrayList<>();for (String name : list) {if (name.startsWith("卡")) {kaList.add(name);}}List<String> shortList = new ArrayList<>();for (String name : kaList) {if (name.length() == 3) {shortList.add(name);}}for (String name : shortList) {System.out.println(name);}
}

这段代码中含有三个循环,每一个作用不同:

1. 首先筛选所有姓卡的人;
2. 然后筛选名字有三个字的人;
3. 最后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?**不是。循环是做事情的方式,而不是目的。**另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。那Lambda的衍生物Stream能给我们带来怎样更加优雅的写法呢?

Stream的更优写法

下面来看一下借助Java 8的Stream API,什么才叫优雅:

public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("卡特琳娜");list.add("卡萨丁");list.add("维迦");list.add("卡兹克");list.add("拉莫斯");list.stream().filter(name -> name.startsWith("卡")).filter(name -> name.length() == 3).forEach(name->System.out.println(name));
}

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

3.2 流式思想概述

注意:请暂时忘记对传统IO流的固有印象!

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

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

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

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

注意:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。

1. 搭建好函数模型,才可以执行函数模型: 一定要有终结的方法,没有终结的方法,这个函数模型是不会执行的
2. Stream流的操作方式也是流动操作的,也就是说每一个流都不会存储元素
3. 一个Stream流只能操作一次,不能重复使用
4. Stream流操作不会改变数据源

3.3 获取流方式

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

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

1. 所有的Collection集合都可以通过stream默认方法获取流;list.stream() set.stream()
2. Stream接口的静态方法of可以获取数组对应的流。int[] array = {1,2,3} Stream.of(array)

3.3.1 根据Collection获取流

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

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

3.3.2 根据Map获取流

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

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.3.3 根据数组获取流

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

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

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

3.4 常用方法

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

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

3.4.1 函数拼接与终结方法

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

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

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

3.4.2 forEach:逐一处理

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

void forEach(Consumer<? super T> action); // 该方法接收一个Consumer接口函数(这是一个功能界面,因此可以用作lambda表达式或方法引用的赋值对象),会将每一个流元素交给该函数进行处理。
public static void main(String[] args) {// 得到流对象String[] names = {"拉莫斯", "皮城女警", "潘森", "提莫"};Stream<String> stream = Stream.of(names);// 调用forEach方法进行逐个处理!/*stream.forEach((String name)->{System.out.println(name);});*/stream.forEach(name-> System.out.println(name));
}

3.4.3 count:统计个数

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

long count(); // 返回一个long值代表元素个数(不再像旧集合那样是int值)。
public static void main(String[] args) {// 得到流对象Stream<String> stream = Stream.of("拉莫斯", "皮城女警", "潘森", "提莫");// 统计个数long count = stream.count();System.out.println(count);}

3.4.4 filter:过滤

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

Stream<T> filter(Predicate<? super T> predicate); // 该接口接收一个Predicate函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件

Stream流中的filter方法基本使用(过Lambda表达式来指定了筛选的条件:必须姓张。)

public static void main(String[] args) {// 定义集合ArrayList<String> list = new ArrayList<>();// 添加数据list.add("卡特琳娜");list.add("卡萨丁");list.add("维迦");list.add("卡兹克");list.add("拉莫斯");// 转成Stream流对象Stream<String> stream = list.stream();// 只要所有姓卡的人Stream<String> sm = stream.filter((String name) -> name.startsWith("卡"));// 逐个处理(打印)sm.forEach(name-> System.out.println(name));}

3.4.5 limit:取用前几个

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

Stream<T> limit(long maxSize); // 参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。
public static void main(String[] args) {// 定义集合ArrayList<String> list = new ArrayList<>();// 添加数据list.add("卡特琳娜");list.add("卡萨丁");list.add("维迦");list.add("卡兹克");list.add("拉莫斯");// 转成Stream流对象Stream<String> stream = list.stream();// 获得前3个元素数据stream.limit(3).forEach(name-> System.out.println(name));}

3.4.6 skip:跳过前几个

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

Stream<T> skip(long n); // 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
public static void main(String[] args) {// 定义集合ArrayList<String> list = new ArrayList<>();// 添加数据list.add("卡特琳娜");list.add("卡萨丁");list.add("维迦");list.add("卡兹克");list.add("拉莫斯");// 转成Stream流对象Stream<String> stream = list.stream();// 获得前3个元素数据stream.limit(3).forEach(name-> System.out.println(name));System.out.println("==================================");// 跳过前2个元素// stream.skip(2).forEach(name-> System.out.println(name)); // 报错!一个Stream流只能操作一次!list.stream().skip(2).forEach(name-> System.out.println(name));System.out.println("==================================");// 获得最后2个元素数据list.stream().skip(list.size()-2).forEach(name-> System.out.println(name));}

3.4.7 map:映射

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

<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流
public static void main(String[] args) {// 定义集合ArrayList<String> list = new ArrayList<>();// 添加数据list.add("110");list.add("119");list.add("120");list.add("114");list.add("112");// 将list转成对应的流【类型是String的】Stream<String> stream = list.stream();// 将stream流映射到另外一个流【类型已经发生变化!类型是Integer】Stream<Integer> stream1 = stream.map((String s) ->{return Integer.parseInt(s);});// 对数据进行加法运算+10stream1.forEach(s-> System.out.println(s+10));}

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

3.4.8 concat:组合

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

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

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

public static void main(String[] args) {// 定义2个数组String[] s1 = {"盲僧","机器人","蒙多","刘禅"};String[] s2 = {"王昭君","貂蝉","虞姬","妲己"};// 将2个数组转成对应的Stream流Stream<String> stream1 = Stream.of(s1);Stream<String> stream2 = Stream.of(s2);// 将2个流合并为一个流Stream<String> stream = Stream.concat(stream1, stream2);// 逐个处理(打印出来)stream.forEach(s-> System.out.println(s));}

3.5.1 传统方式

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

3.6 收集Stream结果

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

3.6.1 收集到集合中

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

* Collectors的静态方法:
public static <T> Collector<T, ?, List<T>> toList():转换为List集合。
public static <T> Collector<T, ?, Set<T>> toSet():转换为Set集合。

代码演示:

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

3.6.2 收集到数组中

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

Object[] toArray();

代码演示:

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

定时器、Lambda表达式、Stream流相关推荐

  1. 第四章 函数式编程(Lambda表达式Stream流)

    一.Lambda表达式 特点:是匿名函数 2是可传递 匿名函数:需要一个函数,但又不想命名一个函数的场景下使用lambda表达式,使用lambda表达式时函数内容应该简单 可传递:将lambda表达式 ...

  2. JDK1.8新特性之Lambda表达式+Stream流+函数式接口

    一.Lambda表达式 Lambda表达式,是JDK1.8引入的一种语法,这种语法可以对匿名内部类的写法,进行简写. 1.快速入门 package org.westos.demo2;import ja ...

  3. lambda表达式——Stream管道流的map操作

    lambda表达式--Stream管道流的map操作 一.回顾Stream管道流map的基础用法 二.处理非字符串类型集合元素 三.再复杂一点:处理对象数据格式转换 四.flatMap 一.回顾Str ...

  4. 精通lambda表达式:java多核编程_Java8 Lambda表达式和流操作如何让你的代码变慢5倍...

    有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格--iterator 和 for-each 循环--比 Java ...

  5. Java8 Lambda表达式和流操作如何让你的代码变慢5倍

    当本文博主也想尝试着去用stream的方式去处理数据,毕竟代码看上去很简洁,很爽,但是,我就写了这么简单的一句: List<DevUsbUpgradeResult> list = inse ...

  6. java 8 stream_深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

  7. Lambda表达式和流API:基本示例

    该博客文章包含基本Lambda表达式和Stream API示例的列表,我在2014年6月在Java用户组– Politechnica Gedanensis (格但斯克技术大学)和Goyello的实时编 ...

  8. Lambda和Stream流

    目录 需要笔记的可以关注私聊,我发给你 Lambda 格式 排序 Stream流 获取流: .stream( ) 操作流(中间方法): 终结流: 收集流的操作 第一部分 第二部分 第三部分 Optio ...

  9. Lambda与Stream流

    1.概述 Lamda和Stream都是jdk8的新特性,十分实用 Lamda是对匿名函数类的简化,极大提高了代码的简洁性 Steam流是对数据进行处理的流 2.Lambda ()->{} 是通用 ...

  10. Lambda表达式_Stream流_File类

    目录 1 Lambda表达式 1.1 体验Lambda表达式 1.2 函数式接口 1.3 Lambda表达式的使用 1.4 Lambda表达式的案例 1.5Lambda表达式的省略模式 1.6Lamb ...

最新文章

  1. 谷歌开源 TFGAN,让训练和评估 GAN 变得更加简单
  2. vba查找数据并返回单元格地址_VBA积木代码中实现反向多值查找、LIKE模糊查找...
  3. Django从理论到实战(part44)--JsonResponse类
  4. pythonwhile爬虫教程_Python 爬虫从入门到进阶之路(十一)
  5. 费诺编码c语言实验报告,formal parameter 4 different from declaration
  6. 华中科技大学2005年计算机组成原理试题,华中科技大学200年计算机组成原理考研试题.doc...
  7. c语言int占几个字节 vc,int类型占几个字节
  8. java面试笔试大全
  9. ffmpeg 处理字幕
  10. java索引越界异常_索引越界异常java
  11. 错误排查:Cloudera Manager Agent 的 Parcel 目录位于可用空间小于 10.0 吉字节 的文件系统上。 /opt/cloudera/parcels...
  12. Xcode8快速注释插件无法使用
  13. 在家做什么挣钱,50个赚钱的热门项目分享
  14. 服务器物理机如何实现系统快照,Lvm快照实现物理备份之自动化
  15. 服务于离群点检测的无监督特征选择值-特征层次耦合模型
  16. Linux各版本内核下载地址
  17. es 安装以及api
  18. 中国嵌入式打印机市场趋势报告、技术动态创新及市场预测
  19. Redis网站热搜关键词加载实践,建议收藏
  20. python day 07

热门文章

  1. 马普尔小姐探案集S02E03【熙阳岭的疑云】
  2. 手把手教你申请lynda高级账号,长期免费使用 lynda.com
  3. Excel COUNT COUNTA区别
  4. mysql 正则 查询 手机号,移动手机号码段 正则
  5. 5月10日云栖精选夜读:阿里专家直击前端盛会JSConf2017 Day2:见证Moment.js精彩分享...
  6. 读书寄语之春天该很好,你若尚在场
  7. Installation of packages ‘stringi’ had non-zero exit status
  8. 弱网测试:使用netem模拟网络延迟、丢包、损坏、重复、和乱序等网络问题
  9. 记录一次服务器技术选型
  10. 【java初学】List集合