常用接口
JDK8中,已经定了一些会常用到的函数式接口,这些函数式接口都定义在javlanfunction包中,例如Predicate、Consumer、Function、Supplier、UnaryOperator和BinaryOperator等。
抽象方法booleantest(Tt)
javutifunctioPredicate接口定义了一个名叫test的抽象方法,他接受泛型T对象,返回一个boolean类型的结果。例如:过滤出数组中大于50的数据
//将数组中大于50的数输出来
public class Test01 {
public static void main(String[] args) {
Integer[] arr = {14,50,56,135,34,20};
Test01 t = new Test01();
//第二个参数是接口Predicate,可以使用lambda表达式代替
Integer[] datas = t.filter(arr, e -> e > 50);
System.out.println(Arrays.toString(datas));
}
//filter方法第一个参数是数组,第二个参数是Predicate接口
public Integer[] filter(Integer[] arr, Predicate predicate){
//定义一个集合来接收符合要求的值
ArrayList list = new ArrayList<>();
for (Integer i : arr) {
//调用接口中的test()方法,返回值是布尔类型,当满足lambda表达式传过来的条件时返回真
if(predicate.test(i)){
list.add(i);
}
}
//将集合转换为数组返回
return list.toArray(new Integer[list.size()]);
}
}
除了抽象方法test()外,Predicate接口中还定义了一些默认方法和静态方法:and(),or(),negate()等
默认方法and(),or(),negate()
and()方法这个方法相当于&&,但是它有返回值,且是Predicate接口,参数类型也是Predicate接口
or()方法这个方法相当于||,返回值是Predicate接口,传入的参数类型也是Predicate接口类型
negate()方法相当于取反,无参数,有返回值。
public class Test02 {
public static void main(String[] args) {
Integer[] arr = {12,3,43,123,34,6,56,7};
//条件1:数据大于10
Predicate p1 = e -> e>10;
//条件2:数据小于50
Predicate p2 = e -> e<50;
//两个条件同时成立 e>10&&e<50
Predicate p_1 = p1.and(p2);
//两个条件中的一个成立:e>10||e<50
Predicate p_2 = p1.or(p2);
//对某个条件取反:e<10
Predicate p_3 = p1.negate();
//调用filter方法
Test02 t = new Test02();
Integer[] result_1 = t.filter(arr, p_1);
System.out.println(Arrays.toString(result_1));
Integer[] result_2 = t.filter(arr, p_2);
System.out.println(Arrays.toString(result_2));
Integer[] result_3 = t.filter(arr, p_3);
System.out.println(Arrays.toString(result_3));
}
public Integer[] filter(Integer[] arr, Predicate predicate){
ArrayList list = new ArrayList<>();
for (Integer i : arr) {
if(predicate.test(i)){
list.add(i);
}
}
return list.toArray(new Integer[list.size()]);
}
}
静态方法removeIf()
JDK8中,给Collection集合增加了默认方法:removeIf,将Predicate作为自己的参数,来移除符合条件的数据,如下具体使用如下
Consumer接口
Consumer单词的意思是消费者的意思,消费者的一种行为叫做消费,可以理解为Consumer接口通过方法accept()来实现这种消费行为,而且一个消费者对象对应一种消费行为。
例如,定义一个方法对学生对象进行操作,学生类中有一个name属性。将这个属性加上前缀,后缀,以及前后缀。
代码实现
补充:jDK8中,给Collection集合增加了默认方法:forEach用来遍历集合,定义如下
仔细看forEach方法的定义可以发现,它传入的是一个Consumer接口对象,并且方法中实现了增强for循环,每次传入进去的对象都会调用accep()方法。
Function接口
javutifunctioFunction
可以看到该Function接口中有一个抽象方法apply(),一个静态方法identity()以及两个默认方法Compose()和andThen(),且都是泛型方法默认的Compose()方法和andThen()方法的作用可以理解为按顺序执行两种操作行为。Compose()方法和andThen()方法的参数是两个Funtion接口对象,返回值是泛型的Funcion
怎么看出是泛型方法的
public class Test03 {
public static void main(String[] args) {
String str = 'a-b-c-a-b-c';
//传入字符串,返回数组,把字符串按照 '-' 进行分割为字符串数组,并返回该数组
//'a-b-c-a-b-c' 转换为 {'a','b','c','a','b','c'}
Function f1 = s -> s.split('-');
//传入字符串数组,返回Set集合,目的是去除数组中重复的数据,把结果存放到Set集合中并返回
Function> f2 = arr -> {
Set set = new HashSet<>();
for(String string : arr){
set.add(string);
}
return set;
};
//f1函数的结果作为f2函数的参数,f1和f2组合成f3函数,f3函数表示传入字符串,返回Set集合
//通过andThen方法可以联合这两个函数,即先分割得到字符串数组,再将得到的字符串数组添加到集合中
Function> f3 = f1.andThen(f2);
//调用f3函数的apply(T t),传入一个字符串,返回set集合
Set set = f3.apply(str);
System.out.println(set);
}
}
关于静态方法identity()方法,就是传入一个值,并返回改值
public class Test {
public static void main(String[] args) {
Function f = Function.identity();
//传入hello,返回hello
String result = f.apply('hello');
System.out.println(result);
}
}
//运行结果:hello
Supplier接口
javutifunctioSupplier接口,Supplier接口中的方法,不需要参数,可以根据我们指定的算法,返回一个数据。例如:输出10个1-100之间的随机的奇数
public class Test {
public static void main(String[] args) {
//生成1-100直接的随机的奇数
Supplier supplier = ()->{
int num ;
do {
num = (int) (Math.random()*100+1);
}while ( (num & 1)==0 );
return num;
};
for (int i = 0; i < 10; i++) {
System.out.println(supplier.get());
}
}
}
补充
Predicate、Consumer,Function
可以看出,该函数式接口,没有定义泛型,里面的方法和Predicate是一样的使用方法,只不过这时候操作的数据直接就是int类型,就不需要再进行自动装箱和拆箱了,提高了运行效率。
例如:
public class Test04 {
public static void main(String[] args) {
int[] arr = new int[10000];
for(int i = 0; i p = num -> num%2 == 0;
// IntPredicate p = num -> num%2 == 0;
long start = System.currentTimeMillis();
for(int i:arr){
p.test(i);
}
long end = System.currentTimeMillis();
System.out.println('共耗时' + (end-start)+'毫秒');
}
}
当数据较大时,分别使用这两个接口,可以看出IntPredicate接口执行效率较高些
默认情况下,这些专门对基本类型数据进行操作的函数式接口,它们的名字都有一定的规律前缀,比如DoublePredicate、IntConsumer、LongBinaryOperator、IntFunction等。其中,Function接口还有针对输出参数类型的变种:ToIntFunction、IntToDoubleFunction等。
文章为作者独立观点,不代表观点