函数式接口
1.概述
1.1 定义
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式接口体现就是Lambda表达式,所以函数式接口就是可以使用Lambda表达式的接口
只有确保接口中有且仅有一个抽象方法,Java中Lambda表达式才能顺利进行推导
1.2 使用
如何检测一个接口是否为函数式接口?
- @FunctionalInterface
- 放在定义接口的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意:
- 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要满足函数式接口定义的条件,也同样是函数式接口。但是,建议加上该注解
//函数式接口:有且仅有一个抽象方法的接口
public class MyInterfaceDemo {
public static void main(String[] args) {
MyInterface my = () -> System.out.println("函数式接口");
my.show();//函数式接口
}
}
@FunctionalInterface
interface MyInterface {
void show();
// void test();
}
2.函数式接口作为参数
需求:
- 定义一个类(RunnableDemo),在类中提供两个方法
- 一个方法是:startThread(Runnable r) 方法参数Runnable是一个函数式接口
- 一个是主方法,在主方法中调用startThread方法
public class RunnableDemo {
public static void main(String[] args) {
//在主方法中调用startThread方法
//匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程启动了");
}
});
//Lambda表达式
startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
}
private static void startThread(Runnable r) {
// Thread t = new Thread(r);
// t.start();
new Thread(r).start();
}
}
通过上面练习,我们知道:
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
-
startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
3.函数式接口作为方法返回值
需求:
- 定义一个类(ComparatorDemo),在类中提供两个方法
- 一个方法是:Comparator getComparator() 方法返回值Comparator是一个函数式接口
- 一个是主方法,在主方法中调用getComparator()方法
package com.advanced.functionalinterface.demo03;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class ComparatorDemo {
public static void main(String[] args) {
//构造场景
ArrayList<String> arr = new ArrayList<String>();
//向集合中添加元素
arr.add("cccc");
arr.add("bb");
arr.add("aaa");
arr.add("d");
//排序前
System.out.println("排序前:"+arr);//排序前:[cccc, bb, aaa, d]
//使用工具类自然排序
// Collections.sort(arr);//排序后:[aaa, bb, cccc, d]
//使用自定义比较器排序
Collections.sort(arr,getComparator());//排序后:[d, bb, aaa, cccc]
//排序后
System.out.println("排序后:"+arr);
}
private static Comparator<String> getComparator() {
//匿名内部类
// return new Comparator<String>() {
// @Override
// public int compare(String s1, String s2) {
// return s1.length()-s2.length();
// }
// };
//Lambda表达式
// return (String s1,String s2) -> {
// return s1.length()-s2.length();
// };
//Lambda简化式
return (s1,s2) -> s1.length()-s2.length();
}
}
通过上面练习,我们知道:
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
-
private static Comparator<String> getComparator() { return (s1,s2) -> s1.length()-s2.length(); }
4.常用的函数式接口
4.1 内容
Java8在java.util.function包下预定义了大量的函数式接口供我们使用
我们重点学习下面的4个接口:
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
4.2 Supplier接口
- public interface Supplier:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
- Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
package com.advanced.functionalinterface.demo04supplier;
import java.util.function.Supplier;
public class SupplierDemo {
public static void main(String[] args) {
String s = getString(() -> "林青霞");
System.out.println(s);
Integer i = getInteger(() -> 30);
System.out.println(i);
}
private static Integer getInteger(Supplier<Integer> i) {
return i.get();
}
private static String getString(Supplier<String> supp) {
return supp.get();
}
}
练习:
- 定义一个类(SupplierTest),在类中提供两个方法
- 一个方法是int getMax(Supplier sup) 返回int数组中的最大值
- 一个是主方法,在主方法中调用getMax方法
package com.advanced.functionalinterface.demo04supplier;
import java.util.function.Supplier;
public class SupplierTest {
public static void main(String[] args) {
//定义一个int[]数组
int[] arr = {10, 90, 30, 40, 20, 50};
int max = getMax(() -> {
int oldmax = arr[0];
for (int i = 0; i < arr.length; i++) {
if (oldmax < arr[i]) {
oldmax = arr[i];
}
}
return oldmax;
});
System.out.println(max);
}
//返回int数组中的最大值
public static int getMax(Supplier<Integer> sup) {
return sup.get();
}
}
4.3 Consumer接口
- public interface Consumer:包含两个方法
- void accept(T t): 对给定的参数执行此操作
- default Consumer andThen(Consumer<? super T> after):返回一个组合的Consumer,按顺序执行该操作,然后执行after操作
- Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
package com.advanced.functionalinterface.demo05consumer;
import java.util.function.Consumer;
public class ConsumerDemo {
public static void main(String[] args) {
// operatorString((s) -> {
// System.out.println(s);
// });
operatorString(s -> System.out.println(s));//林青霞
// operatorString(System.out::println);
operatorString(s -> System.out.println(new StringBuilder(s).reverse().toString()));//霞青林
System.out.println("==========");
operatorString("林青霞",s -> System.out.println(s),s -> System.out.println(new StringBuilder(s).reverse().toString()));
}
//定义一个方法,用不同的方式消费同一个字符串两次
public static void operatorString(String name,Consumer<String> con1,Consumer<String> con2) {
// con1.accept(name);
// con2.accept(name);
con1.andThen(con2).accept(name);
}
//定义一个方法,消费一个字符串数据
public static void operatorString(Consumer<String> con) {
con.accept("林青霞");
}
}
练习:
- String[] strArray = {“林青霞,30”,“张曼玉,35”,“王祖贤,33”};
- 字符串数组中的多条信息,请按格式:“姓名:XX,年龄:XX”的格式信息打印出来
- 要求:
- 把打印姓名的动作作为第一个Consumer接口的Lambda实例
- 把打印年龄的动作作为第二个Consumer接口的Lambda实例
- 将两个Consumer接口按顺序组合在一起使用
package com.advanced.functionalinterface.demo05consumer;
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
//定义一个字符串数组
String[] strArray = {"林青霞,30","张曼玉,35","王祖贤,33"};
//拆分字符串数组并打印
for (int i = 0; i < strArray.length; i++) {
String[] s = strArray[i].split(",");
//将年龄从String类型转换为Integer类型
String name = s[0];
int age = Integer.parseInt(s[1]);
printInfo(name,age,(s1) -> System.out.print("姓名:"+name),(i1)-> System.out.println(", 年龄:"+age));
}
}
//定义一个方法,按指定格式打印字符串数组
private static void printInfo(String name, Integer age, Consumer<String> con1,Consumer<Integer> con2) {
con1.accept(name);
con2.accept(age);
}
}
优化代码:
package com.advanced.functionalinterface.demo05consumer;
import java.util.function.Consumer;
public class ConsumerTest02 {
public static void main(String[] args) {
String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};
// printInfo(strArray, (arr) -> {
// String name = arr.split(",")[0];
// System.out.print("姓名:" + name);
// }, (arr) -> {
// int age = Integer.parseInt(arr.split(",")[1]);
// System.out.println(", 年龄:" + age);
// });
//简化上面代码
printInfo(strArray, str -> System.out.print("姓名:" + str.split(",")[0]),
str -> System.out.println(", 年龄:" + Integer.parseInt(str.split(",")[1])));
}
private static void printInfo(String[] arrays, Consumer<String> con1, Consumer<String> con2) {
for (String arr : arrays) {
con1.andThen(con2).accept(arr);
}
}
}
4.4 Predicate接口
Predicate:常用的四个方法:
- boolean test(T t):在给定的参数上评估这个谓词(判断逻辑由Lambda表达式实现),返回一个布尔值
- default Predicate negate():返回一个逻辑的否定,对应逻辑非
- default Predicate and(Predicate<? super T> other):返回一个组合判断,对用短路与
- default Predicate or(Predicate<? super T> other):返回一个组合判断,对应短路或
- Predicate接口常用于判断参数是否满足指定的条件
test与negate方法:
package com.advanced.functionalinterface.demo06predicate;
import java.util.function.Predicate;
public class PredicateDemo01 {
public static void main(String[] args) {
// boolean b1 = checkString("hello", (s) -> {
// return s.length()>8;
// });
boolean b1 = checkString("hello", s -> s.length() > 8);
System.out.println(b1);//false
boolean b2 = checkString("helloworld", s -> s.length() > 8);
System.out.println(b2);//true
}
//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);//在给定的参数上评估这个谓词(判断逻辑由Lambda表达式实现),返回一个布尔值
// return !pre.test(s);
// return pre.negate().test(s);//default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
}
}
and与or方法:
package com.advanced.functionalinterface.demo06predicate;
import java.util.function.Predicate;
public class PredicateDemo02 {
public static void main(String[] args) {
boolean b1 = checkString("hello", s -> s.length() > 8);
System.out.println(b1);
boolean b2 = checkString("helloworld", s -> s.length() > 8);
System.out.println(b2);
boolean b3 = checkString("hello", s -> s.length() > 8, s -> s.length() < 15);
System.out.println(b3);
boolean b4 = checkString("helloworld", s -> s.length() > 8, s -> s.length() < 15);
System.out.println(b4);
}
//同一个字符串给出两种不同的判断,并将两个判断结果做逻辑与所得的结果作为最终结果
private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
// boolean b1 = pre1.test(s);
// boolean b2 = pre2.test(s);
// boolean b = b1 && b2;
// return b;
// return pre1.and(pre2).test(s);//短路与(逻辑与)
return pre1.or(pre2).test(s);//短路或(逻辑或)
}
//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
}
}
练习:
- String[] strArray = {“林青霞,30”,“柳岩,34”,“张曼玉,35”,“貂蝉,31”,“王祖贤,33”};
- 字符串数组中由多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历集合
- 同时满足以下要求:姓名长度大于2,年龄大于33
package com.advanced.functionalinterface.demo06predicate;
import java.util.ArrayList;
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String[] args) {
//定义字符串数组
String[] strArray = {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};
boolean b = checkString(strArray,s -> s.split(",")[0].length()>2,
s -> Integer.parseInt(s.split(",")[1])>33);
}
//通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历集合
private static boolean checkString(String[] arrays, Predicate<String> pre1, Predicate<String> pre2) {
ArrayList<String> list2 = new ArrayList<String>();
boolean b = false;
for (String s: arrays) {
b = pre1.and(pre2).test(s);
if (b) {
list2.add(s);
}
}
for (String s : list2) {
System.out.println(s);
}
return b;
}
}
改进后:
package com.advanced.functionalinterface.demo06predicate;
import java.util.ArrayList;
import java.util.function.Predicate;
public class PredicateTest02 {
public static void main(String[] args) {
//定义字符串数组
String[] strArray = {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};
ArrayList<String> array = checkString(strArray, s -> s.split(",")[0].length() > 2,
s -> Integer.parseInt(s.split(",")[1]) > 33);
//遍历集合
for (String s : array) {
System.out.println(s);
}
}
//通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList
private static ArrayList<String> checkString(String[] arrays, Predicate<String> pre1, Predicate<String> pre2) {
//创建集合对象
ArrayList<String> list = new ArrayList<String>();
//遍历字符串数组
for (String str : arrays) {
if (pre1.and(pre2).test(str)) {
list.add(str);
}
}
return list;
}
}
4.5 Function接口
Function<T,R>:常用的两个方法:
-
R apply(T t):将此函数应用于给定的参数
-
default Function<T,V> andThen(Function<? super R,? extends V> after):
返回一个组合函数,首先将此函数应用于其输入,然后将
after
函数应用于结果 -
Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
package com.advanced.functionalinterface.demo07function;
import java.util.function.Function;
public class FunctionDemo {
public static void main(String[] args) {
convert("100", s -> Integer.parseInt(s));
convert(100, i -> String.valueOf(i + 566));
convert("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));
}
//定义一个方法,把字符串转为int类型,在控制台输出
private static void convert(String s, Function<String, Integer> fun) {
Integer i = fun.apply(s);
System.out.println(i);
}
//定义一个方法,把int类型数据加一个整数后,转换为字符串在控制台输出
private static void convert(int i, Function<Integer, String> fun) {
String s = fun.apply(i);
System.out.println(s);
}
//定义一个方法,把字符串转为int类型,在加一个整数后,重新转为字符串在控制台输出
private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
// int i = fun1.apply(s);
// String ss = fun2.apply(i);
// System.out.println(ss);
String ss = fun1.andThen(fun2).apply(s);
System.out.println(ss);
}
}
练习:
- String s = “林青霞,30”;
- 请按照指定要求进行操作:
- 将字符串截取得到年龄部分
- 将上一步得到的年龄字符串转为int类型
- 将上一步int类型数据加70,得到一个int结果,在控制台输出
- 通过Function接口来实现函数拼接
package com.advanced.functionalinterface.demo07function;
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
//定义一个字符串
String s = "林青霞,30";
convert(s, s1 -> Integer.parseInt(s.split(",")[1]), i -> i + 70);
}
//通过Function接口来实现函数拼接
private static void convert(String s, Function<String, Integer> fun1, Function<Integer, Integer> fun2) {
Integer age = fun1.andThen(fun2).apply(s);
System.out.println(age);
}
}
5. Stream流
5.1 体验Stream流
需求:按照下面要求完成集合创建和遍历
- 创建一个集合,存储多个字符串元素
- 把集合中所有姓张的元素存储到新集合
- 把张开头长度为3的元素存储到新集合
- 遍历上一步得到的集合
package com.advanced.stream.demo01;
import java.util.ArrayList;
public class StreamDemo {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("张无忌");
list.add("张敏");
//张开头的集合
ArrayList<String> zhanglist = new ArrayList<>();
//遍历集合
for (String s : list) {
if (s.startsWith("张")) {
zhanglist.add(s);
}
}
// System.out.println(zhanglist);//[张曼玉, 张无忌, 张敏]
//长度为3的集合
ArrayList<String> threeList = new ArrayList<>();
for (String s : zhanglist) {
if (s.length() == 3) {
threeList.add(s);
}
}
// System.out.println(threeList);//[张曼玉, 张无忌]
for (String s : threeList) {
System.out.println(s);
}
System.out.println("==========\\(@_@)/==========");
//使用Stream流改进
// list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
}
}
使用wanstream流的方式完成过滤操作
- list.stream().filter(s -> s.startsWith(“张”)).filter(s -> s.length() == 3).forEach(System.out::println);
- 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:生成流、过滤姓张、过滤长度为3、逐一打印
- stream流把真正的函数式编程风格引入到Java中
5.2 Stream流生成方式
Stream流的使用
-
生成流
通过数据源(集合、数组等)生成流
list.stream()
-
中间操作
一个流后面可以跟随一个或多个中间操作,其主要目的是打开流,做出某种程度的过滤/映射,然后返回一个新的流,交给下一个操作使用
filter()
-
终结操作
一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作
forEach()
Stream流常见生成方式
-
Collection体系的集合可以使用默认方法stream()生成流
default Stream stream()
-
Map体系的集合间接生成流
-
数组可以通过Stream接口的静态方法of(T…values)生成流
package com.advanced.stream.demo02;
import java.util.*;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
//Collection体系的集合可以使用默认方法stream()生成流
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
//Map体系的集合间接生成流
Map<String,Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();//通过键生成stream流
Stream<Integer> valueStream = map.values().stream();//通过值生成stream流
Set<Map.Entry<String, Integer>> entryStream = map.entrySet();//通过键值对生成stream流
//数组可以通过Stream接口的静态方法of(T...values)生成流
String s = "hello";
Stream<String> strStream = Stream.of(s);
Stream<String> strArraysStream = Stream.of("hello", "world", "java");
Stream<Integer> intStream = Stream.of(10, 20, 30);
}
}
5.3 Stream流常见中间操作方式
- Stream filter(Predicate<? super T> predicate):返回由与此给定谓词匹配的此流的元素组成的流,用于对流中数据进行过滤
- predicate接口中的方法 boolean test(T t):对给定参数进行判断,返回一个布尔值
package com.advanced.stream.demo03;
import java.util.ArrayList;
public class StreamDemo01 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("张无忌");
list.add("张敏");
//需求1:把list集合中以张开头元素在控制台打印
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
System.out.println("==========");
//需求2:把list集合中长度为3的元素在控制台打印
list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
System.out.println("==========");
//需求3:把list集合中以张开头,长度为3元素在控制台打印
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
}
}
- Stream limit(long maxSize):返回由此流的元素组成的流,截取指定个数的数据,截短长度不能超过 maxSize
- Stream skip(long n):跳过指定参数个数的数据,返回由该流剩余元素组成的流
package com.advanced.stream.demo03;
import java.util.ArrayList;
public class StreamDemo02 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("张无忌");
list.add("张敏");
//需求1:取前3个元素在控制台输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("----------");
//需求2:跳过前3个元素,将剩余的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
System.out.println("----------");
//需求3:跳过前2个元素,将剩余的元素的前2个在控制台输出
list.stream().skip(2).limit(2).forEach(System.out::println);
}
}
- static Stream concat(Stream<? extends T> a, Stream<? extends T> b):创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。即合并a和b两个流为一个流
- Stream distinct():返回由该流的不同元素(根据 Object.equals(Object))组成的流
package com.advanced.stream.demo03;
import java.util.ArrayList;
import java.util.stream.Stream;
public class StreamDemo03 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("张无忌");
list.add("张敏");
//需求1:取前4个元素形成一个流
Stream<String> stream1 = list.stream().limit(4);
//需求2:跳过前2个元素形成一个流
Stream<String> stream2 = list.stream().skip(2);
//需求3:合并需求1和需求2的流,并将结果在控制台输出
// Stream.concat(stream1,stream2).forEach(System.out::println);
//需求4:合并需求1和需求2的流,并将结果在控制台输出,要求不能有重复元素
Stream.concat(stream1,stream2).distinct().forEach(System.out::println);
}
}
- Streamsorted():返回由此流的元素组成的流,根据自然顺序排序
- Stream sorted(Comparator<? super T> comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
package com.advanced.stream.demo03;
import java.util.ArrayList;
public class StreamDemo04 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<>();
list.add("linqingxia");
list.add("zhangmanyu");
list.add("wangzuxian");
list.add("liuyan");
list.add("zhangmin");
list.add("zhangwuji");
//需求1:按照字母顺序在控制台输出
// list.stream().sorted().forEach(System.out::println);
//需求2:按照字符串长度把数据在控制台输出
// list.stream().sorted((s1,s2) -> s1.length()-s2.length()).forEach(System.out::println);
list.stream().sorted((s1,s2) -> {
int num1 = s1.length() - s2.length();
int num2 = num1 == 0 ? s1.compareTo(s2) : num1;
return num2;
}).forEach(System.out::println);
}
}
-
Stream map(Function<? super T,? extends R> mapper):返回由给定函数应用于此流的元素的结果组成的流
Function接口中方法 R apply(T t) -
IntStream mapToInt(ToIntFunction<? super T> mapper):返回一个 IntStream,其中包含将给定函数应用于此流的元素的结果
IntStream:表示原始int流
ToIntFunction接口中方法 int applyAsInt(T value)
package com.advanced.stream.demo03;
import java.util.ArrayList;
public class StreamDemo05 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
//需求:将集合中字符串数据转换为整数后在控制台输出
// list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
// list.stream().map(Integer::parseInt).forEach(System.out::println);
// list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
//IntStream接口中方法 int sum():返回此流中元素的总和
int result = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(result);//150
}
}
5.4 Stream流常见终结操作方法
Stream流常见终结操作方法:
- void forEach(Consumer<? super T> action):对此流的每个元素执行操作
- Consumer接口中方法 void accept(T t): 对给定的参数执行此操作
- long count():返回此流中的元素数
package com.advanced.stream.demo04;
import java.util.ArrayList;
public class StreamDemo {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("张无忌");
list.add("张敏");
//需求1:将集合元素在控制台输出
list.stream().forEach(System.out::println);
//需求2:统计集合中以‘张’开头的元素个数,并将统计的数在控制台输出
long count = list.stream().filter(s -> s.startsWith("张")).count();
System.out.println(count);//3
}
}
5.5 Stream流练习
现在两个ArrayList集合分别存储6名男演员和6名女演员,要求完成如下操作:
- 男演员只要名字长度为3的前3名
- 女演员只要姓林的,并且不要第一个
- 把过滤后的男演员和女演员姓名结合到一起
- 把上一步操作后的元素作为构造方法的参数创建对象,遍历数据
- 演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
package com.advanced.stream.demo05;
import java.util.ArrayList;
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
//创建集合
ArrayList<String> manList = new ArrayList<>();
manList.add("周润发");
manList.add("成龙");
manList.add("刘德华");
manList.add("吴京");
manList.add("周星驰");
manList.add("李连杰");
ArrayList<String> womanList = new ArrayList<>();
womanList.add("林心如");
womanList.add("张曼玉");
womanList.add("林青霞");
womanList.add("柳岩");
womanList.add("林志玲");
womanList.add("王祖贤");
//男演员只要名字长度为3的前3名
Stream<String> man = manList.stream().filter(s -> s.length() == 3).limit(3);
//女演员只要姓林的,并且不要第一个
Stream<String> woman = womanList.stream().filter(s -> s.startsWith("林")).skip(1);
//把过滤后的男演员和女演员姓名结合到一起
Stream<String> actor = Stream.concat(man, woman);
//把上一步操作后的元素作为构造方法的参数创建对象,遍历数据
// actor.forEach(s -> {
// Actor a = new Actor(s);
// System.out.println(a.getName());
// });
// actor.forEach(s -> System.out.println(new Actor(s).getName()));
actor.map(Actor::new).forEach(p -> System.out.println(p.getName()));
}
}
输出结果:
周润发
刘德华
周星驰
林青霞
林志玲
5.6 Stream流收集操作
对数据使用流操作完毕后,我们想把流中数据收集到集合中,该怎么办?
Stream流收集操作:
- R collect(Collector collector)
- 但这个收集方法参数是一个Conllector接口
工具类Collectors提供了具体的收集方法:
- public static Collector<T,?,List> toList():返回一个Collector,它将输入元素收集到一个新的List集合
- public static Collector<T,?,List> toSet():返回一个Collector,它将输入元素收集到一个新的Set集合
- public static Collector<T,?,Map<K,U>> toMap(Function keyMapper,Function valueMapper):返回一个Collector,它将元素累加到一个Map集合,其键和值是将所提供的映射函数应用于输入元素的结果
package com.advanced.stream.demo06;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
/*
//需求1:得到名字长度为3的流
Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
//需求2:把使用Stream流操作完毕的数据收集到List集合并遍历
List<String> names = listStream.collect(Collectors.toList());
for (String name : names) {
System.out.println(name);
}
*/
//创建Set集合对象
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
set.add(35);
set.add(33);
/*
//需求3:得到年龄大于25的流
Stream<Integer> setStream = set.stream().filter(age -> age > 25);
//需求4:把使用Stream流操作完毕的数据收集到Set集合并遍历
Set<Integer> ages = setStream.collect(Collectors.toSet());
for (Integer age : ages) {
System.out.println(age);
}
*/
//定义一个字符串数组,每个数据由姓名和年龄组成
String[] strArrays = {"林青霞,30","张曼玉,35","王祖贤,33","柳岩,25"};
//需求5:得到字符串数组中年龄大于28的流
Stream<String> arrStream = Arrays.stream(strArrays).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
//需求6:把使用Stream流操作完毕的数据收集到Map集合并遍历,字符串姓名作键,年龄作值
Map<String, Integer> map = arrStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
Set<String> keySet = map.keySet();
for (String key : keySet) {
Integer value = map.get(key);
System.out.println(key+","+value);
}
}
}