java8函数式编程

什么是函数式编程

函数式编程是java8的一大特色,也就是将函数作为一个参数传递给指定方法。别人传的要么是基本数据类型,要么就是地址引用 ,我们要穿一个“动作”。

Stream

说到函数式编程,就不得不提及Stream,Stream跟我们熟知的io流可不是同一个东西,泛指可以顺序执行或者并行执行的元素序列,主要是针对集合,可以将多个函数通过“.”串起来执行,其特点如下:

  • stream不会存储数据,只是将集合流化,比如说 声明一个stream之后,往集合里面扔东西,stream可以取到新扔到集合里面的数据,你可以理解为操作时stream会实时从堆中的集合对象提取数据。
  • stream不会改变原集合,我的理解是stream 是一堆元素顺序或者并行执行我们串起来的函数,改变后并不会对集合中的元素造成影响。
  • steam是延迟执行的,也就是说在聚合操作之前 的其他操作,都会阻塞,直到执行聚合函数,其他的函数才开始一并执行。

解析相关接口

我们看看跟函数式编程相关的接口

java.long.FunctionalInterface是一个注解接口,函数接口都会实现它,看看它有什么特别的

从这里我们可以看出来一个函数接口只有一个抽象方法,但是如果要加一切其他的功能怎么办呢?接口中添加功能的话相当麻烦,接口相关实现类都需要修改,接下来“default”就登场了


    default方法只能在接口出现,它不是抽象方法,可以通过methodInstance.isDefault()辨认,咦,弄啥勒,接口还能有普通方法?是的你没看错,添加default方法是为了方便修改接口,不至于每次修改过后都要将他的“儿子,孙子”等实现一一修改;如果在接口中default修饰的方法不加body会怎么样呢?   没错, 会编译失败!贴图:


接口中的default方法不用强制在实现类中 出现,当然业务需要的话,也可以重写来进行多态;

下面是java.util.function中的函数接口描述

序号接口 & 描述
1BiConsumer<T,U>

代表了一个接受两个输入参数的操作,并且不返回任何结果

2BiFunction<T,U,R>

代表了一个接受两个输入参数的方法,并且返回一个结果

3BinaryOperator<T>

代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

4BiPredicate<T,U>

代表了一个两个参数的boolean值方法

5BooleanSupplier

代表了boolean值结果的提供方

6Consumer<T>

代表了接受一个输入参数并且无返回的操作

7DoubleBinaryOperator

代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。

8DoubleConsumer

代表一个接受double值参数的操作,并且不返回结果。

9DoubleFunction<R>

代表接受一个double值参数的方法,并且返回结果

10DoublePredicate

代表一个拥有double值参数的boolean值方法

11DoubleSupplier

代表一个double值结构的提供方

12DoubleToIntFunction

接受一个double类型输入,返回一个int类型结果。

13DoubleToLongFunction

接受一个double类型输入,返回一个long类型结果

14DoubleUnaryOperator

接受一个参数同为类型double,返回值类型也为double 。

15Function<T,R>

接受一个输入参数,返回一个结果。

16IntBinaryOperator

接受两个参数同为类型int,返回值类型也为int 。

17IntConsumer

接受一个int类型的输入参数,无返回值 。

18IntFunction<R>

接受一个int类型输入参数,返回一个结果 。

19IntPredicate

:接受一个int输入参数,返回一个布尔值的结果。

20IntSupplier

无参数,返回一个int类型结果。

21IntToDoubleFunction

接受一个int类型输入,返回一个double类型结果 。

22IntToLongFunction

接受一个int类型输入,返回一个long类型结果。

23IntUnaryOperator

接受一个参数同为类型int,返回值类型也为int 。

24LongBinaryOperator

接受两个参数同为类型long,返回值类型也为long。

25LongConsumer

接受一个long类型的输入参数,无返回值。

26LongFunction<R>

接受一个long类型输入参数,返回一个结果。

27LongPredicate

R接受一个long输入参数,返回一个布尔值类型结果。

28LongSupplier

无参数,返回一个结果long类型的值。

29LongToDoubleFunction

接受一个long类型输入,返回一个double类型结果。

30LongToIntFunction

接受一个long类型输入,返回一个int类型结果。

31LongUnaryOperator

接受一个参数同为类型long,返回值类型也为long。

32ObjDoubleConsumer<T>

接受一个object类型和一个double类型的输入参数,无返回值。

33ObjIntConsumer<T>

接受一个object类型和一个int类型的输入参数,无返回值。

34ObjLongConsumer<T>

接受一个object类型和一个long类型的输入参数,无返回值。

35Predicate<T>

接受一个输入参数,返回一个布尔值结果。

36Supplier<T>

无参数,返回一个结果。

37ToDoubleBiFunction<T,U>

接受两个输入参数,返回一个double类型结果

38ToDoubleFunction<T>

接受一个输入参数,返回一个double类型结果

39ToIntBiFunction<T,U>

接受两个输入参数,返回一个int类型结果。

40ToIntFunction<T>

接受一个输入参数,返回一个int类型结果。

41ToLongBiFunction<T,U>

接受两个输入参数,返回一个long类型结果。

42ToLongFunction<T>

接受一个输入参数,返回一个long类型结果。

43UnaryOperator<T>

接受一个参数为类型T,返回值类型也为T。

除了上面列出的,还有个别函数接口没列出来,例如Comparator(), 但总归是个函数接口都会基于FunctionalInterface注解


总结

这些函数主要还是分为几个主要函数,其余都是以其为基础的分化;

  • Supplier<T> 无参数,返回一个结果。
  • Consumer<T> 代表了接受一个输入参数并且无返回的操作。
  • Function<T,R> 接受一个输入参数,返回一个结果,返回的结果跟参数的类型无关
  • Predicate<T> 接受一个输入参数,返回一个布尔值结果。
  • BinaryOperator<T> 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果,xxxOperator 函数都是接受类型跟返回值类型相同。

而且,从上面五个主要函数接口命名也可以看出其作用,"Bi"作为前缀的都是属于二元操作,接受两个参数。

T1toT2是从接受T1类型参数,返回T2类型结果,比如IntToLongFunction 接受一个int类型参数,返回long类型结果。


实战1

下面我写了一个功能,拳击手有分组跟体重两个属性,然后给一组拳击手集合,根据小组或者体重级排序,用到的是Comparator(),也是一个函数接口

上码:

/**
 * Created by zhanghe
 *      拳击手
 */

public class Boxer {
    //分组
    private String group;
    //重量级
    private Integer weight;

    public Boxer(String group, Integer weight) {
        this.group = group;
        this.weight = weight;
    }

    public static <T> List<List<T>> divider(List<T> datas, Comparator<T> c) {
        //声明一个列表来接收各个分组
        List<List<T>> list = new ArrayList<>();
        for (T t : datas) {
            //通过isSameGroup 标识来判断分组是否已创建
            boolean isSameGroup = false;
            for (int i = 0; i < list.size(); i++) {
                //compare函数返回值为int,正数说明param1>param2,0说明param1=param2,负数说明param1<param2
                //这里用到的原理是将List列表datas中的各项与分组列表list中的元素比较(比较的规则由外面作为参数传递,这就是函数式编程),
                // 值为0表示规则相符即为同一组
                if (c.compare(t, list.get(i).get(0)) == 0) {
                    isSameGroup = true;
                    list.get(i).add(t);
                    break;
                }
            }
            //比较完了发现没有规则相符的,即自成一系
            if (!isSameGroup) {
                List<T> e = new ArrayList<>();
                e.add(t);
                list.add(e);
            }
        }
        return list;
    }

    public static void main(String[] args) {
        List<Boxer> boxers = Arrays.asList(
                new Boxer("红队", 120),
                new Boxer("绿队", 180),
                new Boxer("蓝队", 200),
                new Boxer("绿队", 220),
                new Boxer("蓝队", 120),
                new Boxer("红队", 80),
                new Boxer("红队", 90),
                new Boxer("绿队", 240)
        );
        List<List<Boxer>> dividByGroup = divider(boxers, new Comparator<Boxer>() {
            @Override
            public int compare(Boxer o1, Boxer o2) {
                //分组一样  即认为 相同
                return o1.group.equals(o2.group) == true ? 0 : 1;
            }
        });
        System.out.println("根据小组区分:");
        dividByGroup.stream().forEach(e->{
            System.out.println(e);
        });

        List<List<Boxer>> dividByWeight = divider(boxers, new Comparator<Boxer>() {
            @Override
            public int compare(Boxer o1, Boxer o2) {
                //体重/100 即体重百分位相同 即认为 相同
                return (o1.weight / 100 - o2.weight / 100) == 0 ? 0 : 1;
            }
        });
        System.out.println("根据体重区分:");
        dividByWeight.stream().forEach(e->{
            System.out.println(e);
        });
    }

    @Override
    public String toString() {
        return "Boxer{" +
                "小组='" + group + '\'' +
                ", 体重=" + weight +
                '}';
    }
}

这个是结果



实战2

我用Predicate 写了个具有匹配功能的方法,当然 匹配的规则还是作为一个函数参数传递,撸码不是很优雅,只看功能就好~

闲话不多说,上码:

        List<Object> list = Arrays.asList(1, 2,"",100,"3", 'c');
        List<Object> matchList = match(list, a -> {
            return a instanceof Integer && (Integer) a > 10;
        });
        System.out.println(matchList);//100

        Set set = new HashSet<>();
        set.add("haha");
        set.add(1);
        set.add(100);

        Set matchSet = match(set, a -> a instanceof Integer);
        System.out.println(matchSet);//[1, 100]

        String matchString = match("java", a -> {
            return a instanceof String && ((String) a).startsWith("j");
        });
        System.out.println(matchString);//java
        Integer noMatch = match(100, a -> {
            return a instanceof String && ((String) a).startsWith("j");
        });
        System.out.println(noMatch);//null
    }

    /**
     *
     * @param t 入参  需要匹配规则的参数
     * @param p 函数接口 匹配的动作
     * @param <T>
     * @return 验证成功 是集合 则只留匹配元素,是String,Integer ..匹配成功返回本身,反之返回null
     */
    public static <T> T match(T t, Predicate p) {
        if (t instanceof Collection) {
            if (t instanceof List) {
                t = (T) ((List) t).stream().filter(a -> p.test(a)).collect(Collectors.toList());
            }
            if (t instanceof Set) {
                t = (T) ((Set) t).stream().filter(a -> p.test(a)).collect(Collectors.toSet());
            }
        } else {
            if (!p.test(t)) {
                return null;
            }
        }
        return t;
    }


个人原创,转载请标明出处

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值