一、函数式接口

函数式接口:接口中有且只有一个抽象方法

@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)

将两个流结合为一个新的流