Java总结14 函数式接口Supplier供应者/Consumer消费者/Predicate判断/Function转换和Stream流的概念与应用

一.函数式接口的概念

在这里插入图片描述

函数式接口:接口中只有一个抽象方法.
函数式接口分为4个常用接口
1.Supplier供应者
2.Consumer消费者
3.Predicate判断
4.Function功能(多用于类型转换)

1.Supplier函数接口

概念:需要用到Lambda表达式.定义一个方法,方法里有一个已经存在的类作为其参数和返回值类型.
每当你调用这个方法时,都需要用Lambda表达式去重写,并用return语句返回其已存在类的new对象.包括但不限于retrun的new对象.
目的:目的在于对某个类的功能实现修改,这不同于普通的重写和内部匿名类.而是调用者是谁,谁就重写某个已存在的类对象.
格式:格式: 权限修饰符 动静修饰符 已知类/基本类的返回值类型 自定义方法名(Supplier<泛型> 自定义参数名){ 自定义参数名.get(); }
列如:

  public static ArrayList sp(Supplier<ArrayList> sp) {
        return sp.get();
    }

在这里插入图片描述

代码:

public class SupplierClass {
    public static void main(String[] args) {

     ArrayList array =   sp(()->{
            return new ArrayList();
        });
        System.out.println(array);
    }

    public static ArrayList sp(Supplier<ArrayList> sp) {
        return sp.get();
    }
    
}

2.Predicate函数接口

Predicate函数接口通常用于判断参数是否满足指定的条件
而[参数判断本身的代码]通常都需要自己通过lambda表达式去面向过程来实现.

方法:

boolean test(T t) 对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 多用!!!
default Predicate negate() 返回一个逻辑的否定,对应逻辑非
default Predicate and(Predicateother) 返回一个组合判断,对应短路与

default Predicate or(Predicateother) 返回一个组合判断,对应短路或

创建:
格式形式1:

权限修饰符 static boolean 自定义方法名(参数判断本身的类型 自定义参数判断名,Predicate<参数判断本身的泛型> 自定义Predicate参数名){

   // 自定义Predicate参数名.test(自定义参数判断名);
    // ..negate() ... and().... or()...
}

列如:

 public static boolean getResult(String str, Predicate<String> pr) {

   return pr.test(str); //pr的方法之一 test
    }

格式形式2:

权限修饰符 static boolean 自定义方法名(参数判断本身的类型 自定义参数判断名,Predicate<参数判断本身的泛型> 自定义Predicate参数名1,Predicate<参数判断本身的泛型> 自定义Predicate参数名2){

   // 自定义Predicate参数名.test(自定义参数判断名);
    // ..negate() ... and().... or()...
}

列如:

  public static ArrayList<String> getResultOfArrayList(String[] stringparam, Predicate<String> pr1, Predicate<String> pr2) {
        ArrayList<String> newArray = new ArrayList<>();
        for (String s : stringparam) {
            if (pr1.and(pr2).test(s)) {//'and' 如果都为true
                newArray.add(s);
            }
        }
        return newArray;
    }

在这里插入图片描述
**

3.Function函数接口

注意:Function转换和Consumer消费者都可以通过andThen方法来连续对某个数据进行操作,但Funtion的连续操作之意义重在[转换],即Lambda表达式/方法引用表达式的内容是将其数据进行类型转换的,且结果是被后来表达式所引用的.
Consumer注重消费,即[拿来用/拿来实现]
,前一个表达式处理后的数据,后一个表达式并不会将其拿来用,而是该干嘛干嘛.
**Function:前一个的Lambda表达式/方法引用表达式将数据处理得出结果后,返回给后一个Lambda表达式/引用方法表达式会将引用前一个表达式所得出的结果拿来用.
比如 表达式1: 把100数值的int变量减去50 表达式2:得到还剩下50数值的int变量,进行其它操作.
Consumer:前一个Lambda表达式/方法引用表达式将数据处理后,不会将结果返回给下一个表达式,而下一个表达式拿到的数据则是全新的数据.
比如 表达式1:把一个100数值的int变量减去50,结果为50. 表达式2:得到了全新的,数值为100的int变量.

**
Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
创建
格式:


格式:权限修饰符 动静态修饰符 void 自定义方法名(被转换的参数类型 形参名, Function<泛型1,泛型2> 自定义Function参数1,Function<泛型1,泛型2> 自定义Function参数名2,Function<泛型1,泛型2> 自定义Function参数名3){
//方法体.
}

列如:

    public static void getAgeResult(String str, Function<String,String> fun1,Function<String,Integer> fun2,Function<Integer,Integer> fun3){

  int result = fun1.andThen(fun2).andThen(fun3).apply(str);


    }

方法
andThen
返回一个组合函数,首先将该函数应用于其输入,然后将 after函数应用于结果。
apply
将此函数应用于给定的参数。
compose
返回一个组合函数,首先将 before函数应用于其输入,然后将此函数应用于结果。
Function
返回一个总是返回其输入参数的函数。

:System.out.println(result); //R apply(T t) 将此函数应用于给定的参数 andThen(Function after)
代码:

public class FunctionTest {
    public static void main(String[] args) {
        String nameAndAge = "刘亦菲,39";

        getAgeResult(nameAndAge, (ss)->{
            return ss.split(",")[1];
        }, (ss)->{
            return Integer.parseInt(ss);
        }, (ss)->{
            return ss+80;
        });
    }
    public static void getAgeResult(String str, Function<String,String> fun1,Function<String,Integer> fun2,Function<Integer,Integer> fun3){

        int result = fun1.andThen(fun2).andThen(fun3).apply(str);
        System.out.println(result);
        //R apply(T t) 将此函数应用于给定的参数
        /*
        andThen(Function after)
返回一个组合函数,首先将该函数应用于输入,然后将after函
数应用于结果
         */

    }
}

在这里插入图片描述

4.Consumer函数接口

注意: Consumer注重消费,即[拿来用/拿来实现],前一个表达式处理后的数据,后一个表达式并不会将其拿来用,而是该干嘛干嘛.
而Function转换和Consumer消费者都可以通过andThen方法来连续对某个数据进行操作,但**Funtion的连续操作之意义重在[转换],**即Lambda表达式/方法引用表达式的内容是将其数据进行类型转换的,且结果是被后来表达式所引用的.
Consumer:前一个Lambda表达式/方法引用表达式将数据处理后,不会将结果返回给下一个表达式,而下一个表达式拿到的数据则是全新的数据.
比如 表达式1:把一个100数值的int变量减去50,结果为50. 表达式2:得到了全新的,数值为100的int变量.

Function:前一个的Lambda表达式/方法引用表达式将数据处理得出结果后,返回给后一个Lambda表达式/引用方法表达式会将引用前一个表达式所得出的结果拿来用.
比如 表达式1: 把100数值的int变量减去50 表达式2:得到还剩下50数值的int变量,进行其它操作
.

Consumer函数接口也被称为消费型函数接口,它消费的数据的数据类型由泛型指定.
Consumer只有参数没有返回值.

创建:
作用1/ 用唯一的方式消费参数一次.
格式:

权限修饰符 动静态修饰符 void 自定义方法名(被消费的参数类型 自定义消费参名,Consumer<被消费参数类型之泛型> 自定义Consumer参名){
   //Consumer参名.accept(自定义消费参名);
}

列如:

    public static void getResult(ArrayList<String> ar, Consumer<ArrayList<String>> cr){

  cr.accept(ar);
        }

作用2/ 用多种不同的方式消费参数多次. 这里有两种不同的方式,用这两种方式分别消费参数一次.
格式:

权限修饰符 动静态修饰符 void 自定义方法名(被消费的参数类型 自定义消费参名,Consumer<被消费参数类型之泛型> 自定义Consumer参名1,Consumer<被消费参数类型之泛型> 自定义Consumer参名2){
    //自定义Consumer参名1.andThen(自定义Consumer参名2).accept(自定义消费参名);
        }

列如:

 private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2) {
                 con1.andThen(con2).accept(name);
   }

代码:

public class ConsumerA {
    public static void main(String[] args) {

        ArrayList<String> a = new ArrayList<String>();
        a.add("东");
        a.add("西");
        a.add("南");
        a.add("北");
        getResult(a,(BianLi)->{

            for (String s : BianLi) {
                System.out.println(s);
            }
        });
    }

    public static void getResult(ArrayList<String> ar, Consumer<ArrayList<String>> cr){

        cr.accept(ar);
    }
}

在这里插入图片描述

Stream流

Stream流的概念基于前四者函数接口和Lambda以及方法引用之上所建立的,它将多个Lambda或方法引用的表达式拼成一组,从而达到一连串的的效果.这就是链式编程.
当然,将多个Lambda表达式或方法引用的表达式拼成一起,也是需要Stream流自有的方法去实现的.
如实现一个功能,对ArrayList集合中的元素进行过滤和遍历,那么用Stream流将会是如下样子.
要求:获取所有集合元素中韩姓的人,并且过滤掉名字在2个以上的,然后打印输出.

 ArrayList<String> ar = new ArrayList<>();
        ar.add("张曼玉");
        ar.add("张康");
        ar.add("李国立");
        ar.add("韩国瑜");
        ar.add("韩丽敏");
        ar.add("韩静");
        ar.add("韩冰");
        ar.add("习可");

接着,在Stream流使用了[没有简化的Lambda表达式]情况下,代码简化了很多,不过没看出来有’链式编程’的效果.

//Stream流
        ar.stream().filter((ss)->{
            return ss.startsWith("韩");
        }).filter((ss)->{
            return ss.length()==2;
        }).forEach(System.out::println);

那么,在使用了[简化了的Lambda表达式]情况下,则会是链式编程的效果了.
这里用Lambda表达式的简化特性简化了return语句和大括号.完全达到了链式编程的效果

ar.stream().filter(ss-> ss.startsWith("韩")).filter(ss -> ss.length()==0).forEach(System.out::println);

1.Stream的三个大致步骤

Stream的三个大致步骤分为 开启Stream流 —>中间操作 —>终结Stream流.
其中,'中间操作’和’终结Stream’在对待不同体系的对象类或基本类时,是没区别的.
但是,'Stream开启’流则因对象/类的体系不同而不同,这主要体现在’如何在这个体系的对象/类下开启Stream’流这一点,下面的篇章将会讲解,此处仅叙述三个步骤.
三个步骤的方法名与作用如下:
1/ stream() 作用:开启流

注意:同一个目标(如对象名)下,Stream流不能终结两次,而以下两行代码则都在最后用到了.forEsch()终结语句.

2/ filter() 作用:过滤流,也称之为[中间操作] 当用于集合数组时,会隐形地对元素遍历,再去根据规则过滤

3/ forEach() 终结流,执行括号内的代码,并将过滤后的结果返回给调用处.
count() 终结流,返回此流中的元素数给调用处

在这里插入图片描述

2.流的开启格式之Collection集合体系

所谓Collection体系,就是实现或继承自Collection的集合,如ArrayList集合.
他们在使用到Stream方法时,以对象名.Stream()
格式:Collection体系的对象名.Stream
列如: ar.stream().filter((ss)->{
return ss.startsWith(“韩”);
}).forEach(System.out::println);

参考代码:

public class StreamListTest {
    public static void main(String[] args) {
        ArrayList<String> ar = new ArrayList<>();
        ar.add("张曼玉");
        ar.add("张康");
        ar.add("李国立");
        ar.add("韩国瑜");
        ar.add("韩丽敏");
        ar.add("韩静");
        ar.add("韩冰");
        ar.add("习可");

        //通过Stream流对韩姓开头的元素过滤,然后打印
        ar.stream().filter((ss)->{
            return ss.startsWith("韩");
        }).forEach(System.out::println);
      }
   }     

3.流的开启之数组体系

格式:System.of(数组名)
列如:Stream.of(intArray).forEach(System.out::println);
代码:

public class StreamArray {
    public static void main(String[] args) {
        int[] intArray = {123,21312,5234,312,12,34,121,432,12,213,212,421,21};

                Stream.of(intArray).forEach(System.out::println);
    }
}

4.流的开启之Map集合体系

把Map转成Set集合,间接的生成流
步骤1/ 先创建定义一个Map集合体系,此处以HashMap举例
格式:Map<键泛型,值泛型> 自定义Map集合对象名 = new HashMap<>();
列如:Map<Integer, String> hashmapObj = new HashMap<>();
步骤2/ 将Map集合转为Set集合
格式:Set<泛型> 自定义Set集合对象名 = 自定义Map集合对象名.相关方法();
列如:Set<Map.Entry<Integer, String>> se = hashmapObj.entrySet();
步骤3 利用此Set集合生成Stream流
格式:自定义Set集合对象名.stream()
列如:Map<Integer,String>a = se.stream().limit(3).collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
参考代码:

      //创建Set集合对象

        Map<Integer, String> hashmapObj = new HashMap<>();
        hashmapObj.put(25, "刘亦菲");
        hashmapObj.put(24, "杨颖");
        hashmapObj.put(21, "刘涛");
        hashmapObj.put(27, "杨紫");
        hashmapObj.put(22, "张梦珂");

        //需求1:只要前三个人的信息.

        //间接转换为set,然后间接Stream使用它
        Set<Map.Entry<Integer, String>> se = hashmapObj.entrySet();

        Stream<String> streamObj;
  //然后再收集到Map集合中,也就是转到,赋值到Map集合中.
      Map<Integer,String>a = se.stream().limit(3).collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));


5.方法

1/ 过滤元素或数据
格式:对象名.stream.filter(Lambda表达式/方法引用)
列如:ar.stream().filter(ss-> ss.startsWith("韩")).filter(ss -> ss.length()==0).forEach(System.out::println);

2/ 从上到下数,取前几行指定元素.如limit(4),则为取前4行的元素.
格式:对象名.stream().limit(数值)
列如:Stream limitS = ar.stream().limit(4);

3 略过某几行的元素,数值表示从第一行往最后一行数,要略过几行.
格式:对象名.stream().skip(数值)
列如:ar.stream().skip(2).forEach(System.out::println);

4 将A流和B流的数据(或者说是元素)合并,成为一个新的流
格式:Stream.concat(Stream流A, Stream流B)
列如:Stream.concat(limitS, skipS).forEach(System.out::println);

5 去除重复元素
格式:Stream.concat(Stream流A,Stream流B).distinct();
列如:Stream.concat(limitS, skipS).distinct();

6 根据英文字母,返回元素的升序排序.
格式:对象名.stream().sorted()
列如:ar.stream().sorted().forEach(System.out::println);

7 通过Comparator比较器接口进行自定义排序(可以是重写)
格式:

对象名.stream().sorted(new Comparator<String>() {
             @Override
             public int compare(String o1, String o2) {
                //规则体
             }
         })

列如:

         ar.stream().sorted(new Comparator<String>() {
             @Override
             public int compare(String o1, String o2) {
                int num =  o1.length()-o2.length();
               //谁的值越小,谁靠前,如果都为0,则随意.
                 return num;
             }
         }).forEach(System.out::println);

8 Map扩展方法,在Map方法参数内能够不过滤元素就做其它事情,用于弥补和实现其它功能,如将’元素保存到对象中’,或’元素<----->对象’之间的一些操作
格式:对象名.stream().map(lambda表达式/方法引用)
列如:

 team.map(Student::new).map(Student::toString).forEach(System.out::println);

9 让流获得的元素能够进行运算,如可以通过该方法下的’sum’子方法来进行计算.
应该还有更多的方法,尚未挖掘.

格式:对象名.stream().mapToInt(Integer::parseInt)
列如:ar.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
10 把元素收集到List集合中
public static Collector toList()
尚未尝试.

11 把元素转换到Set集合中
public static Collector toSet()
尚未尝试.

12 把元素转换到Map集合
格式: Map<泛型1泛型2> 自定义map转换对象名 = 原集合对象名.stream().collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
列如:

 Map<Integer,String>a = se.stream().limit(3).collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));

Function函数接口案例

【编号:2306】 使用lambda表达式分别将以下功能封装到Function对象中:
1、将Map<String, Integer>中所有的值存到ArrayList中
2、求存储元素为Integer类型的ArrayList中所有元素的平均数 已知,学生成绩如下:
张三 85
李四 80
王五 90
赵六 95
田七 70
请以学生姓名为key,成绩为value创建集合并存储数据。然后分别使用步骤1和2中所创建的Function对象求出学生的平均成绩。
自己做得!虽然与原答案有点出入,但逻辑相同,完美应用到了Function的使用规范.

public class FunctionAvg {
    public static void main(String[] args) {
        Map<String,Integer> hashObj = new HashMap<>();
        hashObj.put("张三",85 );
        hashObj.put("李四",80);
        hashObj.put("王五",90 );
        hashObj.put("赵六",95 );
        hashObj.put("田七",70 );
      functionMethod(hashObj, (s)->{
          ArrayList<Integer> al = new ArrayList<>();
          for (String s1 : s.keySet()) {
            String keyString = s1;
              al.add(s.get(keyString));
              System.out.println("键和值分别为"+keyString+" "+s.get(keyString));
          }
          return  al;
      },(s)->{
         Integer sum=0;
         int count=0;
          for (Integer integer : s) {
              count++;
              sum +=integer;
          }
          sum= sum/count;
          System.out.println("平均分为"+sum);
          return sum;
      });
    }
    public static Integer functionMethod(Map<String,Integer> mp,Function<Map<String,Integer>,ArrayList<Integer>> fn,Function<ArrayList<Integer>,Integer> fu2){
      return   fn.andThen(fu2).apply(mp);
    }
}

Predicate函数接口案例

【编号:2304】 在Java内置的函数式接口中,Predicate是一个断言型接口,提供了对输入的参数进行断定并返回boolean类型的功能。请阅读如下材料:
在类中,已经提供了filterStr方法,用于过滤集合中的数据,请在主方法中调用该方法,并使用Lambda表达式实现注释中的需求。反馈该题

public class PredicateTwo {
    public static void main(String[] args) {
        List<String> strings = Arrays.asList("123", "123456", "abcdef", "abc123", "ab123cd", "7654321", "123abc", "123s456", "123456789123");
        //  请调用filterStr方法,将其中符合条件的元素保存到新集合
        //  条件一:元素长度为[5-10],包含5和10
        // 条件二:元素只能包含数字字符
        List<String> listObj = filterStr(strings, s -> {

            return s.length() > 4 && s.length() < 11;
        }, s -> {
                int num = 0;
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i)>=48&&s.charAt(i)<=57){
                    num++;
                }
            }
            return num==s.length();
        });

        // 遍历新集合
        for (String s : listObj) {
            System.out.println(s);
        }

    }




    public static List<String> filterStr(List<String> strs, Predicate<String> pre1, Predicate<String> pre2) {
        List<String> list = new ArrayList<>();
        for (String str : strs) {
            if (pre1.and(pre2).test(str))
                list.add(str);
        }
        return list;
    }
}

Consumer函数接口案例

【编号:2302】 在Java内置的函数式接口中,Consumer是一个消费型接口,其中的抽象方法
void accept(T t) // 对给定的参数执行操作,没有返回值
另外,接口中还提供了andThen方法,返回一个组合的Consumer,依次执行操作。请观察如下代码:
public class ConsumerDemo {
public static void main(String[] args) {
// 对姓名sayHello,即将参数前拼接一个 “hello:”,输出

// 对姓名sayHello输出,并打印姓名的长度

}

//accetp方法

//andThen方法
}
}

public class ConsumerTest {
    public static void main(String[] args) {
        String name = "sayHeelo";
        // 对姓名sayHello,即将参数前拼接一个 "hello:",输出

        strMethodOne(name, (s)->{


            System.out.println("hello:"+s);
        });

        // 对姓名sayHello输出,并打印姓名的长度
        strMethodTwo(name, (s1)->{
            System.out.println(s1);
        }, (s2)->{

            System.out.println(" "+s2.length());
        });

    }

    public static void strMethodOne(String str, Consumer<String> cn){
        cn.accept(str);
    }

    public static void strMethodTwo(String str,Consumer<String> cn1,Consumer<String> cn2){
        cn1.andThen(cn2).accept(str);


    }
}

Supplier函数接口案例

【编号:2301】 在Java内置的函数式接口中,Supplier是一个供给型接口,其中的抽象方法:
T get() // 获得结果。每次调用get()方法返回一个T类型对象,没有参数。
请观察如下代码:
public class Test {
public static void main(String[] args) {
// 获取一个32位的UUID(参见java.util.UUID)

// 获取一个字符串形式的当前系统时间毫秒值的id
}

/**
* 可自义的id生成器
* @param supplier
* @return
*/
public static String getId(Supplier supplier) {
return supplier.get();
}
}
很多时候,我们需要获取到一个无重复的id作为某个对象或某条数据的唯一标识。类中,提供了getId方法,请在主方法中调用该方法,分别按要求获取两种id并打印。

public class Test {
    public static void main(String[] args) {
        // 获取一个32位UUID
        String uuid = getId(() -> UUID.randomUUID().toString().replaceAll("-", ""));
        System.out.println(uuid);
        
		// 获取一个以当前系统时间毫秒值为字符串的id
        String timeId = getId(() -> System.currentTimeMillis() + "");
        System.out.println(timeId);
        
    }

    /**
     * 可自义的id生成器
     * @param supplier
     * @return
     */
    public static String getId(Supplier<String> supplier) {
        return supplier.get();
    }
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值