函数式接口、方法引用、Stream流

一、函数式接口

1.概念

函数式接口在Java中是指:有且仅有一个抽象方法的接口。

函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

2.格式

只要确保接口中有且仅有一个抽象方法即可:

修饰符 interface 接口名称 {
	public abstract 返回值类型方法名称(可选参数信息);
	// 其他非抽象方法内容
}

3.@FunctionalInterface注解

与@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:

@FunctionalInterface
public interface MyFunctionalInterface {
	void myMethod();
}

二、常用函数式接口

1.Supplier接口

java.util.function.Supplier接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

1)求数组元素最大值

/**
 * Supplier:供给型接口
 *  T get();
 */
public class Demo02Test {
	//定一个方法,方法的参数传递Supplier,泛型使用Integer
	public static int getMax(Supplier<Integer>sup){
		return sup.get();    
	}
	public static void main(String[] args) {
		int arr[] = {2,3,4,52,333,23};
		//调用getMax方法,参数传递Lambda
		int maxNum = getMax(()>{
		//计算数组的最大值
		int max = arr[0];
		for(int i : arr){
			if(i > max){
			max = i;               
			}           
		}
		return max;        
	});
	System.out.println(maxNum);    
	}
}

2. Consumer接口

java.util.function.Consumer接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

1) 将一个指定的整数放大十倍 打印出来

/**
 * 常见接口
 * Consumer  :消费型接口
 *  void accept(T t);
 */
public class Lambda003 {
    public static void main(String[] args) {
        // 将一个指定的整数放大十倍 打印出来
        Consumer<Integer> c = (i)->{
            System.out.println(i*10);//100
        };
        c.accept(10);
	
        testConsumer((i)->{
            System.out.println(i*10);//200
        },20);
    }

    public static void testConsumer(Consumer<Integer> c, Integer i){
        c.accept(i);
    }
}

3.Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate接口。

1)判断集合中是否所有的数据都是偶数

/**
 * 判断集合中是否所有的数据都是偶数
 * Predicate<T>:断定型接口
 *      boolean test(T t);
 */
public class Lambda006 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(40);
        list.add(44);
        list.add(48);

        //我认为现在都是偶数
        System.out.println(testPredicate((l) -> {
            // 我认为现在都是偶数
            boolean flag = true;
            for (Integer i : l) {
                if (i % 2 != 0) {
                    flag = false;
                    break;
                }
            }
            return flag;
        }, list));//true
    }

    public static boolean testPredicate(Predicate<List<Integer>> pre,List<Integer> list){
        return pre.test(list);
    }
}

4.Function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

1)把Integer集合中的所有的元素都修改一下

/**
 * 存在一个集合, 把Integer集合中的所有的元素 都修改一下  0,1,2,3
 * Function<T, R>
 *     R apply(T t)
 */
public class Lambda005 {
    private List<Integer> list = new ArrayList<>();
    //构造块
    {
        list.add(500);
        list.add(123);
        list.add(356);
        list.add(147);
    }

    public static void main(String[] args) {
        Lambda005 lam = new Lambda005();

        lam.changeElement((j)->{
            return j;
        });
        System.out.println(lam.list);
    }

    public void changeElement(Function<Integer,Integer> fun){
        for(int i=0; i<list.size(); i++){
            list.set(i,fun.apply(i));
        }
    }
}

三、方法引用

若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用是Lambda表达式的另一种表现形式),主要有三种语法格式。

方法引用的注意事项:

  • Lambda体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。

1.对象::实例方法名

/**
 * 方法引用:
 *  Lambda体中调用方法的参数列表和返回值类型,
 *  要与函数式接口中抽象方法的函数列表和返回值类型保持一致
 *
 *  对象::实例方法名
 */
public class MethodCited001 {
    public static void main(String[] args) {
        // 就是打印传入进来的字符串
        Consumer<String> c =(str)->{
            System.out.println(str);
        };
        c.accept("hello,world");

        //方法引用
        PrintStream ps = System.out;
        //ps.println(str);
        Consumer<String> c1 = ps::println;
        c1.accept("你好");
    }
}

2.类::静态方法名

/**
 * 方法引用:
 *  Lambda体中调用方法的参数列表和返回值类型,
 *  要与函数式接口中抽象方法的函数列表和返回值类型保持一致
 *
 *  类::静态方法名
 */
public class MethodCited003 {
    public static void main(String[] args) {
        // 通过lambda判断两个整数的大小,如果第一个数大于第二个数 1,
        //如果第一个数等于第二个数 0, 第一个数小于第二个数 -1

        A a = (i,j)->{
            return (i < j) ? -1 : (i == j) ? 0 : 1;
        };

        //方法引用
        A a1 = Integer::compare;
        System.out.println(a1.com(3,5));//-1
    }
}

3.类::实例方法名

若Lambda参数列表中的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::method。

4.构造器引用

需要调用的构造器的参数列表要与函数式接口中抽象方法参数列表保持一致。

/**
 * 方法引用:
 *  Lambda体中调用方法的参数列表和返回值类型,
 *  要与函数式接口中抽象方法的函数列表和返回值类型保持一致
 *
 *  构造器引用
 */
public class MethodCited004 {
    public static void main(String[] args) {
        // 使用Lambda 生成一个Employee对象
        Supplier<Employee> s = ()->{
            return new Employee();
        };

        //方法引用
        s = Employee::new;//无参
        Employee e = s.get();
        System.out.println(e);//com.shsxt.methodcited.Employee@3f99bd52

        //带参构造
        B<Employee> b = Employee::new;
        Employee e1 = b.get("王五",50,true);
        System.out.println(e1.getName());//王五
    }
}
注:这里用的类和自定义函数式接口没写,看源代码。

四、Stream流

1.获取流

  • 可以通过Conllection系列集合提供的顺序流stream()或并行流parallelStream()
  • 通过Stream类中的静态方法of()
  • 通过Arrays中的静态方法stream()获取数据流

2.Stream的中间操作

1)过滤:filter

可以通过filter方法将一个流转换成另一个子集流。方法签名:

Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个Predicate函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。

2)逐一处理:forEach

虽然方法名字叫forEach,但是与for循环中的“for-each”昵称不同。

void forEach(Consumer<? super T> action);

该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。

/**
 * stream 是数据渠道,用于操作数据源(集合、数组)所生产的元素序列。
 */
public class Stream001 {
    public static void main(String[] args) {
        List<String> list =  new ArrayList<>();
        list.add("ccccc");
        list.add("bb");
        list.add("aaa");
        list.add("dddd");

        //2.通过Arrays中的静态方法stream()获取数据流
        Integer[] is = {1,2,3,4,5};
        Stream<Integer> stream1 = Arrays.stream(is);
        //3.通过Stream类中的静态方法of()
        Stream<String> stream2 =  Stream.of("AAAA","BBBB","CCCCC");

        //1、可以通过Conllection系列集合提供的顺序流stream()或并行流parallelStream()
        Stream<String> stream = list.stream();

        /**
         * Predicate
         *      boolean test(T t)
         *
         * Consumer
         *       void accept(T t)
         *
         *  filter-接收Lambda,从流中排除某些元素  筛选
         */
        stream.filter((t)->{
            // 返回值决定 当前这个元素是否会被放到新的流当中
            //返回true,就会被放到新的流中
            return t.length() > 3;
        }).forEach((t)->{
            System.out.println(t);//ccccc  dddd
        });
    }
}

3)取用几个:limit

limit方法可以对流进行截取,只取用前n个。方法签名:

Stream<T> limit(long maxSize);

参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。

4)跳过前几个:skip

如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:

Stream<T> skip(longn);

如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。

5)去重:distinct

distinct-筛选,通过流所生产元素的hashCode()和equals()去除重复元素

/**
 * stream 是数据渠道,用于操作数据源(集合、数组)所生产的元素序列。
 */
public class Stream002 {
    public static void main(String[] args) {
        List<String> list =  new ArrayList<>();
        list.add("ccccc");
        list.add("bb");
        list.add("aaa");
        list.add("dddd");
        list.add("dddd");
        list.add("dddd");

        //创建流
        Stream<String> stream = list.stream();

        /**
         * limit-截断流,使其元素不超过给定数量
         *      找长度大于2的,并且只找2个,循环遍历
         */
        /*stream.filter((t)->{
            return t.length()>2;
        }).limit(2).forEach((t)->{
            System.out.println(t);//ccccc  aaa
        });*/

        /**
         * skip-跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流。
         *      跳过第一个,遍历剩下的
         */
       /* stream.skip(1).forEach((t)->{
            System.out.println(t);//bb  aaa  dddd dddd dddd
        });*/

        /**
         *  distinct-筛选,通过流所生产元素的hashCode()和equals()去除重复元素
         *          找长度大于3的,去掉重复的元素,遍历
         */

        stream.filter((t)->{
            return t.length()>3;
        }).distinct().forEach((t)->{
            System.out.println(t);//ccccc dddd
        });

    }
}

3.排序

1)sorted(Comparable)-自然排序

2)sorted(Comparator)-定制排序

/**
 * stream 是数据渠道,用于操作数据源(集合、数组)所生产的元素序列。
 */
public class Stream003 {
    public static void main(String[] args) {
        List<String> list =  new ArrayList<>();
        list.add("ccccc");
        list.add("bb");
        list.add("aaa");
        list.add("dddd");

        //创建流
        Stream<String> stream = list.stream();

        /**
         * sorted(Comparable)-自然排序
         */
       /* stream.sorted().forEach((t)->{
            System.out.print(t+" ");//aaa bb ccccc dddd
          });*/

        /**
         * sorted(Comparator)-定制排序
         *      Comparator<T>
         *          int compare(T o1, T o2);
         *      按照字符串长度排序
         */
        stream.sorted((t1,t2)->{
            return t1.length()-t2.length();
        }).forEach((t)->{
            System.out.print(t+" ");//bb aaa dddd ccccc
        });
    }
}

4.终止操作

1) allMatch-检查是否匹配所有元素

2)anyMatch-检查是否至少匹配一个元素

3) noneMatch-检查是否没有匹配所有元素

4)findFirst-返回第一个元素

5) count-返回流中元素的总个数

6)max-返回流中最大值

7)min-返回流中最小值

8) findAny-返回当前流中的任意元素

**
 * stream 是数据渠道,用于操作数据源(集合、数组)所生产的元素序列。
 *
 * 终止操作
 */
public class Stream004 {
    public static void main(String[] args) {
        List<String> list =  new ArrayList<>();
        list.add("ccccc");
        list.add("bb");
        list.add("aaa");
        list.add("dddd");

        //创建流
        Stream<String> stream = list.stream();

        /**
         *  allMatch-检查是否匹配所有元素
         *      Predicate<T>
         *           boolean test(T t)
         */
        /*boolean b = stream.allMatch((t) -> {
            return t.length() > 3;
        });
        System.out.println(b);*///false

        /**
         * anyMatch-检查是否至少匹配一个元素
         *
         * Predicate<T>
         *      boolean test(T t)
         */
        /*System.out.println(stream.anyMatch((t) -> {
            return t.length() > 3;
        }));*///true

        /**
         *  noneMatch-检查是否没有匹配所有元素
         *
         */
        /*System.out.println(stream.noneMatch((t) -> {
            return t.length() > 3;
        }));*///false

        /**
         *  findFirst-返回第一个元素
         *  按长度排序,返回第一个元素
         */
        /*Optional<String> first = stream.sorted((t1, t2) -> {
            return t1.length() - t2.length();
        }).findFirst();

        System.out.println(first.get());*///bb

        /**
         *  count-返回流中元素的总个数
         */
        long count = stream.filter((t) -> {
            return t.length() > 2;
        }).count();
        System.out.println(count);//3
    }
}

5.收集(了解)

  • List-把流中所有元素收集到List中
  • Set-把流中所有元素收集到Set中,删除重复项
  • Map-把流中所有元素收集到Map中,当出现相同的key时会抛异常
public class Stream005 {
    public static void main(String[] args) {
        List<String> list =  new ArrayList<>();
        list.add("ccccc");
        list.add("bb");
        list.add("aaa");
        list.add("dddd");
        list.add("dddd");

        //创建流
        Stream<String> stream = list.stream();
        /**
         * Function<T, R>
         *     R apply(T t)
         */
        Set<String> set = stream.collect(Collectors.toSet());
        set.forEach((t)->{
            System.out.println(t);//bb aaa ccccc dddd
        });
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值