外围概述:在开发中,咱们常常应用匿名外部类作为实参传递参数,咱们能够发现匿名外部类的格局比拟繁琐,那么如何简化呢?本篇咱们将会学习到Lambda表达式来帮忙咱们解决问题。另外咱们也将学习与Lambda表达式相干的函数式接口,以及Stream流。
第一章:Lambda表达式
1.1-函数式编程介绍(理解)
在数学中,函数就是有输入量、输出量的一套计算计划,也就是“拿什么货色做什么事件”。相对而言,面向对象过分强调“必须通过对象的模式来做事件”,而函数式思维则尽量疏忽面向对象的简单语法——强调做什么,而不是以什么模式做。
面向对象的思维: 做一件事件,找一个能解决这个事件的对象,调用对象的办法,实现事件。
函数式编程思维: 只有能获取到后果,谁去做的,怎么做的都不重要,器重的是后果,不器重过程 。
1.2-为什么要用Lambda表达式(理解)
以Runnable为例
当须要启动一个线程去实现工作时,通常会通过java.lang.Runnable
接口来定义工作内容,并应用java.lang.Thread
类来启动该线程。
传统写法:
public class Demo01Runnable {
public static void main(String[] args) {
// 匿名外部类
Runnable task = new Runnable() {
@Override
public void run() {
// 笼罩重写形象办法
System.out.println("多线程工作执行!");
}
};
new Thread(task).start(); // 启动线程
}
}
本着“所有皆对象”的思维,这种做法是无可非议的:首先创立一个 Runnable 接口的匿名外部类对象来指定工作内 容,再将其交给一个线程来启动。
传统写法剖析:
- Thread 类须要 Runnable 接口作为参数,其中的形象 run 办法是用来指定线程工作内容的外围;
- 为了指定 run 的办法体,不得不须要 Runnable 接口的实现类;
- 为了省去定义一个 RunnableImpl 实现类的麻烦,不得不应用匿名外部类;
- 必须笼罩重写形象 run 办法,所以办法名称、办法参数、办法返回值不得不再写一遍,且不能写错;
- 而实际上,仿佛只有办法体才是关键所在。
编程思维的转变
咱们真的心愿创立一个匿名外部类对象吗?不。咱们只是为了做这件事件而不得不创立一个对象。咱们真正心愿做的事件是:将 run 办法体内的代码传递给 Thread 类通晓。
传递一段代码——这才是咱们真正的目标。而创建对象只是受限于面向对象语法而不得不采取的一种伎俩形式。
那有没有更加简略的方法?如果咱们将关注点从“怎么做”回归到“做什么”的实质上,就会发现只有可能更好地达 到目标,过程与模式其实并不重要
Lambda优化体验
借助Java 8的全新语法,上述Runnable
接口的匿名外部类写法能够通过更简略的Lambda表达式达到等效:
public class Demo01LambdaRunnable {
public static void main(String[] args) {
new Thread(() -> System.out.println("多线程工作执行!")).start(); // 启动线程
}
这段代码和方才的执行成果是齐全一样的,能够在1.8或更高的编译级别下通过。从代码的语义中能够看出:咱们启动了一个线程,而线程工作的内容以一种更加简洁的模式被指定。
不再有“不得不创立接口对象”的解放,不再有“形象办法笼罩重写”的累赘,就是这么简略!
1.3-Lambda格局(重要)
格局
-
Lambda省去面向对象的条条框框,格局由3个局部组成:
- 一些参数
- 一个箭头
- 一段代码
- 规范格局:
(参数类型 参数名称) ‐> { 代码语句 }
-
格局阐明:
- 小括号内的语法与传统办法参数列表统一:无参数则留空;多个参数则用逗号分隔。
->
是新引入的语法格局,代表指向动作。- 大括号内的语法与传统办法体要求基本一致。
无参无返回值代码,匿名外部类与lambda比照
代码:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程工作执行!");
}
}).start();
仔细分析该代码中,Runnable
接口只有一个run
办法的定义:
public abstract void run();
即制订了一种做事件的计划(其实就是一个办法):
- 无参数:不须要任何条件即可执行该计划。
- 无返回值:该计划不产生任何后果。
- 代码块(办法体):该计划的具体执行步骤。
同样的语义体现在Lambda
语法中,要更加简略:
() -> System.out.println("多线程工作执行!")
代码阐明:
- 后面的一对小括号即
run
办法的参数(无),代表不须要任何条件; - 两头的一个箭头代表将后面的参数传递给前面的代码;
- 前面的输入语句即业务逻辑代码。
有参有返回值代码,Comparator接口的应用
上面举例演示java.util.Comparator<T>
接口的应用场景代码,其中的形象办法定义为:
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;
// 省略结构器、toString办法与Getter Setter
}
传统写法
如果应用传统的代码对Person[]
数组进行排序,写法如下:
public class Demo05Comparator {
public static void main(String[] args) {
// 原本年龄乱序的对象数组
Person[] array = { new Person("古力娜扎", 19), new Person("迪丽热巴", 18), new Person("马尔扎哈", 20) };
// 匿名外部类
Comparator<Person> comp = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
};
Arrays.sort(array, comp); // 第二个参数为排序规定,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
}
}
}
这种做法在面向对象的思维中,仿佛也是“天经地义”的。其中Comparator
接口的实例(应用了匿名外部类)代表了“依照年龄从小到大”的排序规定。
代码剖析
上面咱们来搞清楚上述代码真正要做什么事件。
- 为了排序,
Arrays.sort
办法须要排序规定,即Comparator
接口的实例,形象办法compare
是要害; - 为了指定
compare
的办法体,不得不须要Comparator
接口的实现类; - 为了省去定义一个
ComparatorImpl
实现类的麻烦,不得不应用匿名外部类; - 必须笼罩重写形象
compare
办法,所以办法名称、办法参数、办法返回值不得不再写一遍,且不能写错; - 实际上,只有参数和办法体才是要害。
Lambda写法
public class Demo06ComparatorLambda {
public static void main(String[] args) {
Person[] array = {
new Person("古力娜扎", 19),
new Person("迪丽热巴", 18),
new Person("马尔扎哈", 20) };
Arrays.sort(array, (Person a, Person b) -> {
return a.getAge() - b.getAge();
});
for (Person person : array) {
System.out.println(person);
}
}
}
省略格局
省略规定
在Lambda规范格局的根底上,应用省略写法的规定为:
- 小括号内参数的类型能够省略;
- 如果小括号内有且仅有一个参,则小括号能够省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都能够省略大括号、return关键字及语句分号。
1.4-Lambda的前提条件(理解)
Lambda的语法十分简洁,齐全没有面向对象简单的解放。然而应用时有几个问题须要特地留神:
- 应用Lambda必须具备接口,且要求接口中有且仅有一个形象办法。
无论是JDK内置的Runnable
、Comparator
接口还是自定义的接口,只有当接口中的形象办法存在且惟一时,才能够应用Lambda。 - 应用Lambda必须具备接口作为办法参数。
也就是办法的参数或局部变量类型必须为Lambda对应的接口类型,能力应用Lambda作为该接口的实例。
有且仅有一个形象办法的接口,称为“函数式接口”。
第二章:函数式接口
2.1-概述(理解)
介绍
函数式接口在Java中是指:有且仅有一个形象办法的接口。
函数式接口,即实用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是能够实用于Lambda应用的接口。只有确保接口中有且仅有一个形象办法,Java中的Lambda能力顺利地进行推导。
从利用层面来讲,Java中的Lambda能够看做是匿名外部类的简化格局。
格局
只有确保接口中有且仅有一个形象办法即可:
修饰符 interface 接口名称 {
public abstract 返回值类型 办法名称(可选参数信息);
// 其余非形象办法内容
}
public abstract 能够省略
FunctionalInterface注解
与@Override
注解的作用相似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface
。该注解可用于一个接口的定义上:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
一旦应用该注解来定义接口,编译器将会强制查看该接口是否的确有且仅有一个形象办法,否则将会报错。不过,即便不应用该注解,只有满足函数式接口的定义,这依然是一个函数式接口,应用起来都一样。
2.2-罕用的函数式接口(重点)
JDK提供了大量罕用的函数式接口以丰盛Lambda的典型应用场景,它们次要在java.util.function
包中被提供。上面是最简略的几个接口及应用示例。
Supplier接口
java.util.function.Supplier<T>
接口,它意味着”供应” , 对应的Lambda表达式须要“对外提供”一个合乎泛型类型的对象数据。
形象办法 : get
仅蕴含一个无参的办法:T get()
。用来获取一个泛型参数指定类型的对象数据。
public class Demo08Supplier {
private static String getString(Supplier<String> function) {
return function.get();
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
System.out.println(getString(() -> msgA + msgB));
}
}
求数组元素最大值
应用Supplier
接口作为办法参数类型,通过Lambda表达式求出int数组中的最大值。提醒:接口的泛型请应用java.lang.Integer
类。
代码示例:
public class DemoIntArray {
public static void main(String[] args) {
int[] array = { 10, 20, 100, 30, 40, 50 };
printMax(() -> {
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
});
}
private static void printMax(Supplier<Integer> supplier) {
int max = supplier.get();
System.out.println(max);
}
}
Consumer接口
java.util.function.Consumer<T>
接口则正好相同,它不是生产一个数据,而是生产一个数据,其数据类型由泛型参数决定。
形象办法:accept
Consumer
接口中蕴含形象办法void accept(T t)
,意为生产一个指定泛型的数据。根本应用如:
import java.util.function.Consumer;
public class Demo09Consumer {
private static void consumeString(Consumer<String> function , String str) {
function.accept(str);
}
public static void main(String[] args) {
consumeString(s -> System.out.println(s));
}
}
Function接口
java.util.function.Function<T,R>
接口用来依据一个类型的数据失去另一个类型的数据,前者称为前置条件,后者称为后置条件。有进有出,所以称为“函数Function”。
形象办法:apply
Function
接口中最次要的形象办法为:R apply(T t)
,依据类型T的参数获取类型R的后果。应用的场景例如:将String
类型转换为Integer
类型。
public class Demo11FunctionApply {
private static void method(Function<String, Integer> function, Str str) {
int num = function.apply(str);
System.out.println(num + 20);
}
public static void main(String[] args) {
method(s -> Integer.parseInt(s) , "10");
}
}
Predicate接口
有时候咱们须要对某种类型的数据进行判断,从而失去一个boolean值后果。这时能够应用java.util.function.Predicate<T>
接口。
形象办法:test
Predicate
接口中蕴含一个形象办法:boolean test(T t)
。用于条件判断的场景,条件判断的规范是传入的Lambda表达式逻辑,只有字符串长度大于5则认为很长。
public class Demo15PredicateTest {
private static void method(Predicate<String> predicate,String str) {
boolean veryLong = predicate.test(str);
System.out.println("字符串很长吗:" + veryLong);
}
public static void main(String[] args) {
method(s -> s.length() > 5, "HelloWorld");
}
}
第三章:Stream流
在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有汇合类库既有的弊病。
3.1-为什么要用Stream流(理解)
传统汇合的多步遍历代码
简直所有的汇合(如Collection
接口或Map
接口等)都反对间接或间接的遍历操作。而当咱们须要对汇合中的元素进行操作的时候,除了必须的增加、删除、获取外,最典型的就是汇合遍历。例如:
public class Demo10ForEach {
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循环的循环体才是“做什么”
为什么应用循环?因为要进行遍历。但循环是遍历的惟一形式吗?遍历是指每一个元素逐个进行解决,而并不是从第一个到最初一个依次解决的循环。前者是目标,后者是形式。
试想一下,如果心愿对汇合中的元素进行筛选过滤:
- 将汇合A依据条件一过滤为子集B;
- 而后再依据条件二过滤为子集C。
那怎么办?在Java 8之前的做法可能为:
这段代码中含有三个循环,每一个作用不同:
- 首先筛选所有姓张的人;
- 而后筛选名字有三个字的人;
- 最初进行对后果进行打印输出。
public class Demo11NormalFilter {
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);
}
}
}
每当咱们须要对汇合中的元素进行操作的时候,总是须要进行循环、循环、再循环。这是天经地义的么?不是。循环是做事件的形式,而不是目标。另一方面,应用线性循环就意味着只能遍历一次。如果心愿再次遍历,只能再应用另一个循环从头开始。
那,Lambda的衍生物Stream能给咱们带来怎么更加优雅的写法呢?
Stream的更优写法
上面来看一下借助Java 8的Stream API,什么才叫优雅:
public class Demo12StreamFilter {
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(s -> System.out.println(s));
}
}
间接浏览代码的字面意思即可完满展现无关逻辑形式的语义:获取流、过滤姓张、过滤长度为3、逐个打印。代码中并没有体现应用线性循环或是其余任何算法进行遍历,咱们真正要做的事件内容被更好地体现在代码中。
3.2-流式思维(理解)
留神:请临时遗记对传统IO流的固有印象!
整体来看,流式思维相似于工厂车间的“生产流水线”。
当须要对多个元素进行操作(特地是多步操作)的时候,思考到性能及便利性,咱们应该首先拼好一个“模型”步骤 计划,而后再依照计划去执行它。
这张图中展现了过滤、映射、跳过、计数等多步操作,这是一种汇合元素的解决计划,而计划就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的办法,能够从一个流模型转换为另一个流模型。而最右侧的数字3是最终后果。
3.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;
/*
获取Stream流的形式
1.Collection中 办法
Stream stream()
2.Stream接口 中静态方法
of(T...t) 向Stream中增加多个数据
*/
public class Demo13GetStream {
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();
}
}
形式2: 依据数组获取流
如果应用的不是汇合或映射而是数组,因为数组对象不可能增加默认办法,所以Stream
接口中提供了静态方法of
,应用很简略:
import java.util.stream.Stream;
public class Demo14GetStream {
public static void main(String[] args) {
String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
Stream<String> stream = Stream.of(array);
}
}
of
办法的参数其实是一个可变参数,所以反对数组。
3.4-罕用办法(重点)
流模型的操作很丰盛,这里介绍一些罕用的API。这些办法能够被分成两种:
- 终结办法:返回值类型不再是
Stream
接口本身类型的办法,因而不再反对相似StringBuilder
那样的链式调用。本大节中,终结办法包含count
和forEach
办法。 - 非终结办法:返回值类型依然是
Stream
接口本身类型的办法,因而反对链式调用。(除了终结办法外,其余办法均为非终结办法。)
更多办法,请自行参考API文档。
逐个解决办法:forEach
尽管办法名字叫 forEach ,然而与for循环中的“for-each”昵称不同。
void forEach(Consumer<? super T> action);
该办法接管一个 Consumer 接口函数,会将每一个流元素交给该函数进行解决。
java.util.function.Consumer<T>接口是一个消费型接口。
Consumer接口中蕴含形象办法void accept(T t),意为生产一个指定泛型的数据。
根本应用
public class Test01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.stream().forEach(name-> System.out.println(name));
}
}
过滤办法:filter
能够通过 filter 办法将一个流转换成另一个子集流。办法签名:
Stream<T> filter(Predicate<? super T> predicate);
该接口接管一个 Predicate 函数式接口参数(能够是一个Lambda或办法援用)作为筛选条件。
温习Predicate接口
java.util.stream.Predicate
函数式接口,其中惟一的形象办法为:
boolean test(T t);
该办法将会产生一个boolean值后果,代表指定的条件是否满足。如果后果为true,那么Stream流的 filter 办法 将会留用元素;如果后果为false,那么 filter 办法将会舍弃元素。
根本应用
Stream流中的 filter 办法根本应用的代码如:
public class Test02 {
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("张"))
.forEach(name-> System.out.println(name));
}
}
在这里通过Lambda表达式来指定了筛选的条件:必须姓张。
映射办法:map
如果须要将流中的元素映射到另一个流中,能够应用 map
办法。办法签名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口须要一个 Function 函数式接口参数,能够将以后流中的T类型数据转换为另一种R类型的流。
温习Function接口
此前咱们曾经学习过 java.util.stream.Function
函数式接口,其中惟一的形象办法为:
R apply(T t);
这能够将一种T类型转换成为R类型,而这种转换的动作,就称为“映射”
根本应用
public class Test03 {
public static void main(String[] args) {
String[]strs = {"11","22","33","44"};
Stream.of(strs)
.map((s -> Integer.parseInt(s)))
.forEach(i-> System.out.println(i+2));
}
}
这段代码中, map 办法的参数通过办法援用,将字符串类型转换成为了int类型(并主动装箱为 Integer 类对 象)。
统计个数办法:count
正如旧汇合 Collection 当中的 size 办法一样,流提供 count 办法来数一数其中的元素个数:
long count();
该办法返回一个long值代表元素个数(不再像旧汇合那样是int值)。根本应用:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张三丰");
list.add("王五");
list.add("张无忌");
long i = list.stream()
.filter(name->name.startsWith("张"))
.count();
System.out.println(i);//3
}
取用前几个办法:limit
limit 办法能够对流进行截取,只取用前n个。办法签名:
Stream<T> limit(long maxSize);
参数是一个long型,如果汇合以后长度大于参数则进行截取;否则不进行操作。根本应用:
public class Test06 {
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("张"))
.limit(2)
.forEach(name-> System.out.println(name));
}
}
// 后果-张三、张三丰
跳过前几个办法:skip
如果心愿跳过前几个元素,能够应用 skip 办法获取一个截取之后的新流:
Stream<T> skip(long n);
如果流的以后长度大于n,则跳过前n个;否则将会失去一个长度为0的空流。根本应用:
public class Test07 {
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("张"))
.skip(2)
.forEach(name-> System.out.println(name));
}
}
// 后果:张无忌
组合办法,concat
如果有两个流,心愿合并成为一个流,那么能够应用 Stream 接口的静态方法 concat :
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
这是一个静态方法,与 java.lang.String 当中的 concat 办法是不同的。
该办法的根本应用代码如:
public class Test08 {
public static void main(String[] args) {
Stream<String> s1 = Stream.of("张三","李四");
Stream<String> s2 = Stream.of("王五","赵六");
Stream.concat(s1,s2).forEach(name-> System.out.println(name));
}
}
// 后果:张三、李四、王五、赵六
流转汇合办法,collect
从Stream流对象转成汇合对象,应用Stream
接口办法collect:
public class StreamDemo06 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("张无忌");
list.add("周芷若");
list.add("张三丰");
Stream<String> stream = list.stream();
//Stream流对象collect()传递Collectors静态方法toList() 流对象元素转成汇合
List<String> newList = stream.filter( s->s.startsWith("张")).collect(Collectors.toList());
System.out.println(newList);
}
}
3.5-综合案例(练习)
需要
当初有两个ArrayList
汇合存储队伍当中的多个成员姓名,要求应用传统的for循环(或加强for循环)顺次进行以下若干操作步骤:
- 第一个队伍只有名字为3个字的成员姓名;
- 第一个队伍筛选之后只有前3集体;
- 第二个队伍只有姓张的成员姓名;
- 第二个队伍筛选之后不要前2集体;
- 将两个队伍合并为一个队伍;
- 打印整个队伍的姓名信息。
两个队伍(汇合)的代码如下:
public class Demo21ArrayListNames {
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("张三");
// ....
}
}
传统形式
应用for循环 , 示例代码:
public class Demo22ArrayListNames {
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);
// 打印整个队伍的姓名信息。
for (String name : totalNames) {
System.out.println(name);
}
}
}
运行后果为:
宋远桥
苏星河
洪七公
张二狗
张天爱
张三
Stream形式
等效的Stream流式解决代码为:
public class Demo23StreamNames {
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).forEach(s->System.out.println(s));
}
}
运行成果齐全一样:
宋远桥
苏星河
洪七公
张二狗
张天爱
张三
3.6-函数拼接与终结办法(理解)
发表回复