JAVA函数式接口与Stream流
函数式接口概述
函数式接口:有且仅有一个抽象方法的接口
- java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda表达式的接口
- 只有确保接口中有且只有一个抽象方法,java中的Lambda表达式才能顺利的进行推导
函数式接口的注解
- @FunctionalInterface
- 放在接口定义的上方,如果接口是函数式接口则编译通过,反之失败
- 这个标注可加可不加,不标注的时候,只有一个抽象方法的接口虽然也是函数式接口,但是标注后能让其他人更加清晰的分辨以及方便编译,建议加上
函数式接口作为方法的参数
public class Printable1 {
public static void main(String[] args) {
//采用匿名内部类
starRunnable(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":线程启动了");
}
});
//Lambda表达式
starRunnable(()-> System.out.println(Thread.currentThread().getName()+":线程启动了"));
}
private static void starRunnable(Runnable r) {
new Thread(r).start();
}
}
-
此处线程的Runnable接口是一个只有run()抽象方法的函数式接口,作为starRunnable()方法的参数
-
进而可以使用匿名内部类甚至是Lambda表达式简写
-
也就是说多线程的时候,因为Runnable是函数式接口,我们不需要去创建对象实现多线程。直接Lambda实现
函数式接口作为方法的返回值
public class ComparatorDemo {
public static void main(String[] args) {
//构造使用场景
//定义集合,储存字符串元素
ArrayList<String> arr = new ArrayList<String>();
arr.add("aaaaa");
arr.add("bb");
arr.add("ddd");
arr.add("ccccccc");
System.out.println("排序前" + arr);
//此排序方法需要传入一个list集合对象,以及可以加入一个比较器接口的实现类对象
// getComparator()方法返回的就是一个Comparator接口匿名实现类对象
Collections.sort(arr, getComparator());
System.out.println("排序后" + arr);
}
private static Comparator<String> getComparator() {
// Comparator<String> com = new Comparator<String>() {
// @Override
// public int compare(String s1, String s2) {
// return s1.length()-s2.length();
// }
// };
//简化后返回一个Comparator接口的匿名实现类对象
// 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();
// };
//进一步简化
return (s1, s2) -> s1.length() - s2.length();
}
}
- 如果方法的返回值是一个函数式接口,我们可以用Lambda表达式作为结果返回(将上面的代码精简一下)
private static Comparator<String> getComparator() {
return (s1, s2) -> s1.length() - s2.length();
}
常用的函数式接口
Supplier接口
练习
- 定义一个类SupplierTest,在类中提供两个方法
- 一个是:int getMax(Supplier
sup) 用于返回一个int集合中的最大值 - 一个是主方法,主方法中使用getMax方法
- 一个是:int getMax(Supplier
public class SupplierTest {
public static void main(String[] args) {
ArrayList<Integer> num = new ArrayList<Integer>();
num.add(5);
num.add(9);
num.add(6);
num.add(2);
num.add(11);
int maxNum = getMax(() -> {
Integer max = num.get(0);
for (Integer i : num) {
if (i > max) {
max = i;
}
}
return max;
});
System.out.println("该int集合中最大的数字是:"+maxNum);
}
private static int getMax(Supplier<Integer> sup) {
return sup.get();
}
}
Consumer接口
消费就是拿已经有的使用,生产就是创造没有的
public class ConsumerDemo {
public static void main(String[] args) {
//依次是匿名内部类,Lambda表达式,方法引用
operatorString("nihaooo", new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
operatorString("nihaooo",s->System.out.println(s));
operatorString("nihaooo",System.out::println);
//反转字符串
operatorString("nihaooo",s -> System.out.println(new StringBuilder(s).reverse()));
//消耗两次
System.out.println("--------------");
operatorString("nihaooo",s -> System.out.println(s),s-> System.out.println(new StringBuilder(s).reverse()));
}
//定义一个方法,用andThen方法消费同一个字符串数据2次
private static void operatorString(String name, Consumer<String> con1,Consumer<String> con2) {
// con1.accept(name);
// con2.accept(name);
con1.andThen(con2).accept(name);
}
//定义一个方法,消费一个字符串数据
private static void operatorString(String name, Consumer<String> con) {
con.accept(name);
}
}
练习
经范例通过Lambda表达式简化后代码
public class ConsumerDemo {
public static void main(String[] args) {
String[] strArr = {"xuzhiyuan,20","shijuanjun,24","ximu,27"};
//使用String类的spilt分割逗号获取姓名年龄分别输出
operatorString(strArr,str -> System.out.print("姓名:"+str.split(",")[0]),
str -> System.out.println(",年龄:"+str.split(",")[1]));
}
private static void operatorString(String[] strArr, Consumer<String> con1, Consumer<String> con2) {
for (String str : strArr) {
con1.andThen(con2).accept(str);
}
}
}
Predicate接口
- Predicate接口通常用于判断参数是否满足指定的条件
- test()与negate()方法的使用
public class PredicateDemo {
public static void main(String[] args) {
//判断字符长度是否大于8
boolean b1 = checkString("hello", s -> s.length() > 8);
System.out.println(b1);
boolean b2 = checkString("djaosdjasdasd", s -> s.length() > 8);
System.out.println(b2);
}
public static boolean checkString(String s, Predicate<String> pre) {
// return pre.test(s);正常的判断逻辑
// return !pre.test(s);可以用!返回相反逻辑
return pre.negate().test(s);//接口自身的方法也可以返回非的逻辑
}
}
- and()方法和or()方法的使用
public static void main(String[] args) {
boolean b1 = checkString("hello", s -> s.length() > 8);
System.out.println(b1);
boolean b2 = checkString("djaosdjasdasd", s -> s.length() > 8);
System.out.println(b2);
System.out.println("------------");
boolean b3 = checkString("dasdasd", s -> s.length() > 8, s -> s.length() < 15);
System.out.println(b3);
}
//同一个字符串给出两个不同判断,最后把这两个判断的结果与运算的结果作为最终的结果
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
// boolean p1 = pre1.test(s);
// boolean p2 = pre2.test(s);
// boolean b = p1 && p2;
// return b;
//可以用接口自带的and or方法写成以下方式
return pre1.and(pre2).test(s) && pre1.or(pre2).test(s);
}
public static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
}
}
练习
-
String[]strArr = {"xuzhiyuan,30","ximu,19","tianming,24","demmo,21","shijuanjun,18"};
-
字符数组有多条信息,通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中
-
满足要求:名字长度大于5,年龄大于20
public class PredicateDemo {
public static void main(String[] args) {
String[] strArr = {"xuzhiyuan,30", "ximu,19", "tianming,24", "demmo,21", "shijuanjun,18"};
//通过2个Predicate接口的匿名内部类简写为Lambda表达式重写,切割字符数组为姓名年龄分别判断条件
//将返回的集合新建对象接收
ArrayList<String> arry = checkString(strArr, s -> s.split(",")[0].length() > 5, s -> Integer.parseInt(s.split(",")[1]) > 20);
//输出集合在控制台
System.out.println("满足名字长度大于5个字符,年龄大于20的人有:" + arry);
}
//同一个字符串给出两个不同判断,最后把这两个判断的结果与运算的结果作为最终的结果
public static ArrayList<String> checkString(String[] strArr, Predicate<String> pre1, Predicate<String> pre2) {
ArrayList<String> arry = new ArrayList<>();
for (String s : strArr) {//if括号里使用接口的and方法判断2个条件都为为true的话
if (pre1.and(pre2).test(s)) {
arry.add(s);//将遍历出来的姓名加年龄的字符串添加到arry集合里
}
}
return arry;//返回整理好的arry集合
}
}
Function接口
public class FunctionDemo {
public static void main(String[] args) {
convert("123", s -> Integer.parseInt(s));
// convert("abc",Integer::parseInt);进行方法引用后
convert(100, i -> String.valueOf(i + 567));
// convert(100,String::valueOf);方法引用简化
convert("234", s -> Integer.parseInt(s), s -> String.valueOf(s + 789));
// convert("234",Integer::parseInt,String::valueOf);方法引用简化
}
//定义一个方法,把一个字符串转换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 str = fun.apply(i);
System.out.println(str);
}
//定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String str = fun1.andThen(fun2).apply(s);
System.out.println(str);
}
}
练习
public class FunctionDemo01 {
public static void main(String[] args) {
String s = "林青霞,30";
//最简化后
strMaker(s, str -> str.split(",")[1], Integer::parseInt, str -> str + 70);
}
private static void strMaker(String str, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
System.out.println(fun1.andThen(fun2).andThen(fun3).apply(str));
}
}
Stream流
体验stream
public class StreamDemo {
public static void main(String[] args) {
ArrayList<String> strArr = new ArrayList<String>();
strArr.add("张无忌");
strArr.add("徐志远");
strArr.add("张路");
strArr.add("汐木");
strArr.add("张小凡");
ArrayList<String> zh = new ArrayList<String>();
// for (String s : strArr) {
// if((s.startsWith("张"))){
// zh.add(s);
// }
//// if("张".equals(String.valueOf(s.charAt(0)))){
//// zh.add(s);
//// 通过charAt获取第一个下标姓氏的字符char类型,转换成string后equals判断是否是张也可完成
//// 但过于繁琐,使用starwith方法可以更加快速判断
//// };
// }
// System.out.println(zh);
//
// ArrayList<String> zh1 = new ArrayList<String>();
//
// for (String str : zh) {
// if(strMaker(str, ss -> ss.length() == 3)){
// zh1.add(str);
// }
// }
// System.out.println(zh1);
System.out.println("--------");
//使用stream改进
strArr.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
}
private static boolean strMaker(String ss, Predicate<String> pre){
boolean b = pre.test(ss);
return b;
}
}
Stream流的生成方式
Stream流的使用
-
生成流
- 通过数据源(集合,数组等)生成流
- 对象.stream()
-
中间操作
- 一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流给下一个操作使用
- filter()
-
终结操作
- 一个流只能有一个操作,当这个操作执行后,流就被使用光了无法再被操作,所以这必定是流的最后一个操作
- forEach()
Stream流的生成方式
public static void main(String[] args) {
//Collection体系的集合可以使用默认方法stream()生成流
List<String> list = new ArrayList<String>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
//Map体系的集合间接的生成流
Map<Integer,String> map = new HashMap<Integer,String>();
Stream<Integer> keyStream = map.keySet().stream();//键对象对应的流
Stream<String> valueStream = map.values().stream();//值对象对应的流
Stream<Map.Entry<Integer, String>> entryStream = map.entrySet().stream();//键值对对象的流
//数组可以通过stream接口的静态方法
String[] str = {"nihao","ooooo","dasda"};
Stream<String> str1 = Stream.of(str);
//可以传可变参数
Stream<String> stream1 = Stream.of("dasda", "fqdqw", "fqfdqwd");
Stream<Integer> intstream = Stream.of(10, 20, 40, 50);
}
Stream流常见的中间操作方法--filter
-
Stream
filter(Predicate<? super T> predicate) :用于对流中的数据进行过滤 - 传入Predicate接口的对象后使用函数接口中的 boolean test(T t):对给定的参数进行判断,返回一个布尔值
-
参考“体验Stream”部分和以下的截图实例
Stream流常见的中间操作方法--limit&skip
- Stream
limit(long maxSize) 返回一个包含该流的元素组成的流,截断长度不超过 maxSize。 - Stream
skip(long n) 跳过指定参数的个数的数据,返回由该流的剩余元素组成的流
public static void main(String[] args) {
ArrayList<String> strArr = new ArrayList<String>();
strArr.add("张无忌");
strArr.add("徐志远");
strArr.add("张路");
strArr.add("汐木");
strArr.add("张小凡");
//取前三个数据在控制台输出
strArr.stream().limit(3).forEach(System.out::println);
System.out.println("---------");
//跳过3个元素,把剩下的元素在控制台输出
strArr.stream().skip(3).forEach(System.out::println);
System.out.println("---------");
//跳过2个元素,把剩下元素的前2个在控制台输出
strArr.stream().skip(2).limit(2).forEach(System.out::println);
}
Stream流常见的中间操作方法--concat&distinct
-
static
Stream concat(Stream<? extends T> a, Stream<? extends T> b) - 合并a,b两个流为一个流
-
Stream
distinct() 返回由该流的不同(不重复)的元素(根据 Object.equals(Object))组成的流。
public static void main(String[] args) {
ArrayList<String> strArr = new ArrayList<String>();
strArr.add("张无忌");
strArr.add("徐志远");
strArr.add("张路");
strArr.add("汐木");
strArr.add("张小凡");
strArr.add("天明");
//1.取前4个数据组成一个流
Stream<String> s1 = strArr.stream().limit(4);
//2.跳过两个数据组成一个流
Stream<String> s2 = strArr.stream().skip(2);
//3.合并1,2需求的流为一个流,并把结果输出在控制台
Stream.concat(s1, s2).forEach(System.out::println);
//4.合并1,2需求的流为一个流,并把结果输出在控制台,要求字符串元素不能重复
Stream.concat(s1, s2).distinct().forEach(System.out::println);
}
Stream流常见的中间操作方法--sorted
- Stream
sorted() 返回由该流的元素组成的流,按自然顺序排序。 - Stream
sorted(Comparator<? super T> comparator) 返回一个包含该流的元素流,根据提供的 Comparator排序。
public static void main(String[] args) {
ArrayList<String> strArr = new ArrayList<String>();
strArr.add("qdnqwdqwdq");
strArr.add("netrbewrbvewbv");
strArr.add("ebervwvwecv");
strArr.add("qwfqfqfvqw");
strArr.add("gtrjnetbwev");
strArr.add("bfewfeqfcqefc");
//1,按照字母顺序把数据输出在控制台
strArr.stream().sorted().forEach(System.out::println);
System.out.println("----------------");
//2.按照字符串长度把数据输出在控制台输出
strArr.stream().sorted((s1, s2) -> {
int num = s1.length() - s2.length();
int num1 = num == 0 ? s1.compareTo(s2) : num;
return num1;
}).forEach(System.out::println);
}
Stream流常见的中间操作方法--map&mapToInt
Stream map(Function<? super T,? extends R> mapper) 返回一个流,包括将给定函数应用到该流元素的结果组成的流。 - Function接口中的方法 R apply(T t)
- IntStream mapToInt(ToIntFunction<? super T> mapper) 返回一个Instream其中包含给定函数应用于此流的元素的结果
- Intstream:表示原始int流
- ToIntFunction接口中的方法 int applyAsInt(T value)
public static void main(String[] args) {
ArrayList<String> strArr = new ArrayList<String>();
strArr.add("10");
strArr.add("20");
strArr.add("30");
strArr.add("40");
strArr.add("50");
//将集合中的字符串转换为整数之后在控制台输出//soutc:方法引用输出快捷
strArr.stream().map(Integer::parseInt).forEach(System.out::println);
strArr.stream().mapToInt(Integer::parseInt).forEach(System.out::println);//两个方法都可以实现
//int sum()返回此流中元素的总和,但这个方法实在intstream下的, 需要通过mapToInt方法转成int后的包含结果的intstream使用sum方法
int result = strArr.stream().mapToInt(Integer::parseInt).sum();
System.out.println(result);
}
Stream流常见的终结操作方法--forEach&count
- void forEach(Consumer<? super T> action) 对该流的每个元素执行动作。
- Consumer接口中的方法 void accept(T t)
- long count():返回此流中的元素数
Stream的综合练习
public static void main(String[] args) {
ArrayList<String> str1 = new ArrayList<String>();
ArrayList<String> str2 = new ArrayList<String>();
str1.add("林青霞");
str1.add("林楚蓉");
str1.add("吴立");
str1.add("李欣");
str1.add("林初心");
str1.add("貂蝉");
str2.add("石田立");
str2.add("天明");
str2.add("杜天");
str2.add("刘德华");
str2.add("林俊杰");
str2.add("蔡明宪");
//男演员只要前面3个字的前三人
Stream<String> man = str2.stream().filter(s -> s.length() == 3).limit(3);
//女演员只要姓林的,除了第一个都要
Stream<String> woman = str1.stream().filter(s -> s.startsWith("林")).skip(1);
//把过滤后的男女演员合并到一起
Stream<String> actorStream = Stream.concat(man, woman);
//把上一步的合并流作为构造方法的参数创建演员对象,并遍历
Stream<Actor> actorStream1 = actorStream.map(s -> new Actor(s));
actorStream1.forEach(s -> System.out.println(s.getActor()));
//简化后
actorStream.map(Actor::new).forEach(s -> System.out.println(s.getActor()));
//最终把所有代码简化为一条代码
Stream.concat(str2.stream().filter(s -> s.length() == 3).limit(3),str1.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).forEach(s -> System.out.println(s.getActor()));
}
Stream流的收集操作
public static void main(String[] args) {
ArrayList<String> str1 = new ArrayList<String>();
str1.add("林青霞");
str1.add("林楚蓉");
str1.add("吴立");
str1.add("李欣");
//得到名字为3个字的流
str1.stream().filter(s -> s.length() == 3).collect(Collectors.toList()).forEach(System.out::println);
//创建set数组
Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(20);
set.add(30);
set.add(40);
set.add(55);
//得到数字大于25的流并收集到一个新的hashset集合并遍历
set.stream().filter(s -> s > 25).collect(Collectors.toSet()).forEach(System.out::println);
System.out.println("-------");
//创建字符串数组
String[] str3 = {"xuzhiyuan,24", "tianming,30", "ximu,29", "shijuanjun,33"};
//通过数组获取Stream的方法,获取大于28岁的字符串并收集到新的map集合,获取键值后遍历
Map<String, String> map = Stream.of(str3).filter(s -> Integer.parseInt(s.split(",")[1]) > 28).collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
Set<String> key = map.keySet();
for (String s : key) {
System.out.println(s+","+map.get(s));
}
}
标签:JAVA,String,Stream,strArr,System,接口,add,out
From: https://www.cnblogs.com/joeximu/p/16722372.html