一、函数式接口
函数式接口:接口中有且只有一个抽象方法
@FunctionalInterface//注解:检测是否为函数式接口
public interface Demo01 {
public abstract void method();
}
Lambda表达式详情在多线程中提到
二、函数式编程
2.1、Lambda的延迟执行
有些场景的代码执行后,结果不一定被使用,从而造成性能的浪费。而Lambda表达式时延迟执行的,它会将代码的执行延迟到一个合适的时间点,即调用的时候。
public class Demo02 {
public static void main(String[] args) {
Hello hello = ()-> System.out.println("hello");
//...
hello.method();//在合适的时候调用
}
}
public interface Hello {
public abstract void method();
}
三、常用函数式接口
1、Supplier接口
Interface Supplier
生产型接口
抽象方法:get()
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
public static String getMassage(Supplier<String> s){
return s.get();
}
public static void main(String[] args) {
String s1 = getMassage(()->{return "你好呀,小帅哥";});
String s2 = getMassage(()->"快来玩呀,小帅哥");
System.out.println(s1+"\n"+s2);
}
练习:求数组最大值
public static Integer get(Supplier<Integer> mem){
return mem.get();
}
public static void main(String[] args) {
int[] arr = {131,432,51,531,3416,565,764};
Integer integer = get(() -> {
int max = 0;
for (int i : arr) {
if (i > max) {
max = i;
}
}
return max;
});
System.out.println(integer);
}
2、Consumer接口
抽象方法:accept(T t)
消费型接口
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
练习:实现字符串反转
public static void method(String name, Consumer<String> s){
s.accept(name);
}
public static void main(String[] args) {
method("123",(String str)->{
String string = new StringBuffer(str).reverse().toString();
System.out.println(string);
});
}
输出结果:321
默认方法:andThen
作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费
例如:
public static void method(String s, Consumer<String> con1, Consumer<String> con2){
//con1.accept(name);
//con2.accept(name);
//使用默认方法andThen连接
//写在前面的先执行
con1.andThen(con2).accept(s);
}
public static void main(String[] args) {
method("Hello",
//con1
(String s1)->{
System.out.println(s1.toLowerCase());
},
//con2
(String s2)->{
System.out.println(s2.toUpperCase());
});
}
练习:
将给定的字符串数组按照指定格式输出
“张三,男”, “李四,男”, “王麻子,女”
public static void method(String[] massage, Consumer<String> con1, Consumer<String> con2{
for (String s : massage) {
con1.andThen(con2).accept(s);
}
}
public static void main(String[] args) {
String[] massage = {"张三,男", "李四,男", "王麻子,女"};
//要对数组中的元素进行裁剪的工作,根据中文格式的逗号(,)分割,调用split()方法
method(massage,
(String string)->{
String name = string.split(",")[0];
System.out.print("姓名:"+name+"。");
},
(String string)->{
String gender = string.split(",")[1];
System.out.println("性别:"+gender+"。");
});
}
3、Predicate接口
需要对某种数据类型进行判断,从而得到一个boolean值的结果,就可以使用Predicate接口
抽象方法:test
public static boolean method(String str, Predicate<String> pre){
return pre.test(str);
}
public static void main(String[] args) {
String string = "123456";
boolean method = method(string,
(String str) ->
{
boolean i = (str.length()>5)?false:true;
return i;
});
System.out.println(method);
String str = "1";
}
默认方法:and
将两个predicate条件使用&&连接起来实现与的效果
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
练习:如果一字符串的长度在(5,10)内,返回true
public static boolean method(String str, Predicate<String> pre1, Predicate<String> pre2)
{
return pre1.and(pre2).test(str);
}
public static void main(String[] args) {
String string = "123456";
boolean method = method(string,
(String str) ->
{
boolean i = (str.length()<10)?true:false;
return i;
},
(String str)->{
boolean i = (str.length()>5)?true:false;
return i;
});
System.out.println(method);
String str = "1";
}
默认方法:or( Predicate<? super T> other )
或
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
默认方法:negate()
非
default Predicate<T> negate() {
return (t) -> !test(t);
}
静态方法:isEqual(Object tragetRef)
练习:集合信息筛选
数组中有多条姓名+性别的信息,如下:通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,同时需要满足:
1、必须为女生
2、名字必须时4个字
public static ArrayList<String> method(String[] message, Predicate<String> name, Predicate<String> gender){
ArrayList<String> list = new ArrayList<>();
for (String s : message) {
if (name.and(gender).test(s)) {
list.add(s);
}
}
return list;
}
public static void main(String[] args) {
String[] arr = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"};
ArrayList<String> list = method(arr,
string->{
String name = string.split(",")[0];
boolean is = name.length()==4?true:false;
return is;
},
string->{
String gender = string.split(",")[1];
if("女".equals(gender))
return true;
return false;
});
System.out.println(list);
}
4、Function接口
java.util.function.Function<T,R>
接口用来根据一个类型的数据得到另一个类型的数据。前者称为前置条件,后者称为后置条件。
抽象方法:apply
Function接口中最主要的方法
R apply(T t),根据类型T的参数获取类型R的结果。
将String类型的“123”,转换为Integer类型,
public static void method(String s, Function<String, Integer> function){
Integer inte = function.apply(s);
System.out.println(inte);
}
public static void main(String[] args) {
method("1234",string->{
return Integer.parseInt(string);
});
}
默认方法:andThen
进行组合操作(和Consumer中的默认方法andThen效果相同)
练习:
自定义函数模型拼接
请使用Function进行函数拼接,按顺序需要执行的多个函数操作为:String str = “赵丽颖,20”
1、将字符串截取年龄的部分,得到字符串
2、将上一步的字符串转换为int类型的数字
3、将上一步的数字+100,得到结果int数字
public static void method(String string, Function<String, Integer> fun){
Integer i = fun.apply(string);
i+=100;
System.out.println(i);
}
public static void main(String[] args) {
String str = "赵丽颖,20";
method(str,string->{
String s = string.split(",")[1];
int i = Integer.parseInt(s);
return i;
});
Stream流
JDK1.8后出现的,关注的是做什么,而不是怎么做
Stream流属于管道流,只能被消费(使用)一次
第一个Stream流调用完毕方法,数据就会流转到下一个Stream上,
而这时,第一个Stream流一已经使用完毕,就会关闭,所以第一个Stream流就不能调用方法。
ArrayList<String> list = new ArrayList<>();
list.add("张鱼哥");
list.add("张三");
list.add("李四");
list.add("王麻子");
list.add("张零一");
list.add("张零二");
/*
ArrayList<String> listA = new ArrayList<>();
for (String s : list) {
if(s.startsWith("张"))
listA.add(s);
}
//筛选出姓张且名字为三个字人
ArrayList<String> listB = new ArrayList<>();
for (String s : listA) {
if(s.length() == 3)
listB.add(s);
}
for (String s : listB) {
System.out.println(s);
}
*/
//使用stream流
list.stream().filter(name->name.startsWith("张"))
.filter(name->name.length() == 3)
.forEach(name->System.out.println(name));
}
将集合或数组转化为流
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();
Map<String, String> map = new HashMap<>();
//获取键ketSet()
Set<String> keymap = map.keySet();
Stream<String> key = keymap.stream();
//过去值values()
Collection<String> values = map.values();
Stream<String> mapValues = values.stream();
//获取键值对(值与键的映射关系,entrySet())
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream = entries.stream();
//将数组准换为流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
Integer[] arr = {1,2,3,4,5,6};
Stream stream3 = Stream.of(arr);
}
常用方法
- 延迟方法:返回值类型依然是Stream接口自身类型的方法,因此可支持链式调用(除了终结方法外,其他的方法皆为延迟方法)
- 终结方法:返回值类型不再是Stream接口自身类型的方法,本节中主要的中介方法为
count()
和forEach()
,其他终结方法参考Interface Stream
逐一处理:forEach
void forEach(Consumer<? super T> action)
该方法接收的是一个Consumer接口函数,会将每一个流元素交给该函数进行处理
Consumer是一个消费型的函数式接口,可以传递Lambda表达式,消费数据
其实就是用来遍历流中的数据
过滤:filter
Stream<T> filter(Predicate<? super T> predicate)
用于对Stream流中的数据经行过滤
例:
public static void main(String[] args) {
Stream<String> stringStream = Stream.of("张零一", "张一零", "张一一", "李四", "王麻子");
stringStream.filter(name->name.startsWith("张")).forEach(name-> System.out.println(name));
//IllegalStateException: stream has already been operated upon or closed
//stringStream.filter(name->name.length()==3).forEach(name-> System.out.println(name));
}
映射:map
<R> Stream<R> map(Function<? super T,? extends R> mapper)
将流中的某一个元素映射到另一个流中,就可以使用map方法。
例:
统计个数:count
long count()
统计Stream流中的元素个数
例:
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7);
System.out.println(integerStream.count());//7
取用前几个:limit
Stream<T> limit(long maxSize)
使用limit对流中的元素经行截取
例:
public static void main(String[] args) {
Stream<String> stringStream = Stream.of("张零一", "张一零", "张一一", "李四", "王麻子");
Stream<String> limit = stringStream.limit(3);
limit.forEach(num-> System.out.println(num));//张零一 张一零 张一一
}
跳过前几个:skip
Stream<T> skip(long n)
例:
Stream<String> stringStream = Stream.of("张零一", "张一零", "张一一", "李四", "王麻子");
Stream<String> limit = stringStream.skip(3);
limit.forEach(num-> System.out.println(num));//李四 麻子
组合:concat
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
将两个流结合为一个新的流