Java8新特性:函数式接口、方法引用、强大的StreamAPI

目录

函数式接口

什么是函数式(Functional)接口?

Java内置四大核心函数式接口

方法引用与构造器的使用

强大的StreamAPI

Stream到底是什么呢?

Stream的操作三个步骤

创建Stream方式一:通过集合

创建Steam方式二:通过数组

创建Stream的方式三:通过Stream的of()

Stream的中间操作 - 筛选与切片

Stream的中间操作 - 映射

Stream的中间操作 - 排序

Stream 的终止操作


函数式接口

什么是函数式(Functional)接口?

只包含一个抽象方法的接口,称为函数式接口

比如Runnable接口,接口里只包含一个抽象接口的run()方法,同时类上有@FunctionalInterface注解。

@FunctionalInterface注解可以检查它是否是一个函数式接口,同时Javadoc也会包含一条声明,说明这个接口是一个函数式接口。

函数式接口其实就是为了便于写lambda表达式而产生的。

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

如果要使用Lambda表达式,就一定要使用函数式接口,因为lambda表达式的本质就是作为函数式接口的实例。

Java内置四大核心函数式接口

函数式接口参数类型返回类型用途

Consumer<T>

消费型接口

Tvoid

对类型为T的对象应用操作,包含方法

void accpet(T t)

Supplier<T>

供给型接口

T

返回类型为T的对象,包含方法:

T get()

Function<T,R>

函数型接口

TR

对类型为T的对象应用操作,并返回结果。结果为R类型的对象,包含方法

R apply(T t)

Predicate<T>

断定型接口

Tboolean

确定类型为T的对象是否满足某约束,并返回boolean值,包含方法:

boolean值。包含方法:

boolean test(T t)

上面一大段看起来挺抽象的,用举例子的方式来说明。

实例(一) Function 函数式接口

    public static void main(String[] args) {
        // Function<T,R> T:函数的输入类型 R:函数的返回类型
        // Function 匿名实现类的写法
        Function<String,String> function = new Function<String,String>() {
            @Override
            public String apply(String str) {
                return str;
            }
        };

        System.out.println(function.apply("函数式接口"));

        // 函数式接口 lambda表达式的写法
        Function function1=(str)->{return str;};
        System.out.println(function1.apply("lambda表达式"));
    }

实例(二)Predicate 断定型接口

    public static void main(String[] args) {
        // 断定型接口 Predicate 传入一个指定类型的参数,返回一个经过判定后的布尔值
        // 匿名内部类写法
        Predicate<String> predicate = new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return str.isEmpty();
            }
        };
        System.out.println(predicate.test("test"));

        // lambda表达式的写法
        Predicate<String> predicate1 =str->str.isEmpty();
        System.out.println(predicate1.test(""));
    }

实例(三)Consumer 消费型接口

    public static void main(String[] args) {
        // Consumer 消费型接口 接受单个输入参数但不返回结果
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String o) {
                System.out.println(o);
            }
        };
        consumer.accept("打印");
        // lambda表达式写法
        Consumer<String> consumer1 = str->System.out.println(str);
        consumer1.accept("打印");
    }

实例(四)Supplier 供给型接口

    public static void main(String[] args) {
        // 供给型接口 没有参数 有返回值
        // 匿名实现类的写法
        Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                return 1024;
            }
        };
        System.out.println(supplier.get());

        Supplier<Integer> supplier2 =()->{ return 1024; };
        System.out.println(supplier2.get());
    }

方法引用与构造器的使用

当要传递给lambda体的操作,已经有实现方法了,就可以使用方法引用。

方法引用可以看做是lambda表达式的深层次的表达,方法引用就是lambda表达式,也就是函数式接口的实例,可以通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

使用要求:实现接口的抽象的参数列表和返回值类型,必须与方法引用的参数列表和返回值类型保持一致。

方法体  System.out.println(str); 已经有了实现方法,是PrintStream中 void printLn(T t)方法,因此它可以使用方法引用

使用格式:类或者对象::方法名

       // 消费型接口 Consumer中的 void accept(T t)
        Consumer<String> consumer = str->{ System.out.println(str);};
        /**
         * PrintStream中的void println(T t)
         * System.out.println() 是PrintStream中 void printLn(T t)方法
         * 因此我们这个System.out.println(str)方法体中已经有了lambda表达式的实现了,所以可以使用方法引用
         * 使用格式:类或者对象::方法名
         */
        Consumer<String> consumer2 = System.out::println;

强大的StreamAPI

Java8中的两个最为重要的改变就是lambda表达式以及StreamAPI

SteamAPI(java.util.stream)把真正的函数式编程风格引入到Java中,可以写出高效率、干净、简洁的代码。

使用StreamAPI对集合数据进行操作,就类似于使用SQL执行数据库查询,也可以使用SteamAPI并行执行操作,简而言之,SteamAPI提供了高效、易于使用的处理数据的方式。

Stream到底是什么呢?

Stream是数据渠道,用于操作数据源(集合,数组等)所产生的元素序列。

集合讲的是数据,Stream讲的是计算

Stream的操作三个步骤

  • 创建Stream
    • 一个数据源(集合、数组),获取一个流
  • 中间操作
    • 一个中间操作链,对数据源的数据进行处理
  • 终止操作
    • 一旦终止操作,就执行中间操作链,并产生结果,之后不会再被使用

创建Stream方式一:通过集合

Java8中的Collection接口被拓展,提供了两个获取流的方法

default Stream<E> Stream():返回一个顺序流

default Steam<E> parallelStream:返回一个并行流

    /**
     * 创建流的第一种方式 通过集合
     *
     * @param args
     */
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User("小沈", 12));
        userList.add(new User("小丁", 13));
        userList.add(new User("小蟹", 14));
        // 返回一个顺序流
        Stream<User> stream = userList.stream();
        // 返回一个并行流
        Stream<User> parallelStream = userList.parallelStream();
    }

创建Steam方式二:通过数组

java8中的Arrays的静态方法steam()可以获取数组流

static<T> Stream<T> stream(T[] array):返回一个流

    /**
     * 创建Stream的第二种方式 通过数组
     */
    @Test
    public void test() {
        int[] arr = new int[]{1, 2, 3, 4, 5};
        // 得到一个Integer类型的数组流
        IntStream stream = Arrays.stream(arr);
        User user1 = new User("小沈", 12);
        User user2 = new User("小蟹", 12);

        User[] userarry = new User[]{user1, user2};
        // 得到一个User类型的数组流
        Stream<User> stream1 = Arrays.stream(userarry);
    }

创建Stream的方式三:通过Stream的of()

    /**
     * 创建Stream方式三: 通过Stream的of()
     */
    @Test
    public void test3() {
        // 得到一个Integer类型的流
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
    }

Stream的中间操作 - 筛选与切片

多个中间操作可以连起来形成一个流水线,触发流水线上触发终止操作,否则中间操作不会执行任何处理,而在终止操作时一次性全部处理,称为惰性求值。

1.筛选与切片

方法描述
filter(Predicate p)接受Lambda,从流中排出某些元素
distinct()筛选,通过流所产生元素的hashCode()和equals()去重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补

实例(一) filter方法测试

Lambda表达式我总也用不熟练,所以我采用了老式写法和Lambda写法,以熟悉Lambda表达式。

    @Test
    public void test4() {
        List<User> userList = new ArrayList<>();
        userList.add(new User("小沈", 1));
        userList.add(new User("小丁", 11));
        userList.add(new User("小蟹", 21));
        // 返回一个顺序流
        Stream<User> stream = userList.stream();
        // 过滤出userList里age>10的元素 匿名实现类写法
        stream.filter(new Predicate<User>() {
            @Override
            public boolean test(User user) {
                return user.getAge()>10;
            }
        }).forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
            }
        });
        Stream<User> stream2 = userList.stream();
        // 过滤出userList里age>10的元素 Lambda表达式写法
        stream2.filter(u->u.getAge()>10).forEach(System.out::println);
    }

实例(二)distinc()方法测试

    @Test
    public void test5() {
        List<User> userList = new ArrayList<>();
        userList.add(new User("小蟹", 1));
        userList.add(new User("小沈", 1));
        userList.add(new User("小丁", 11));
        // 重复元素
        userList.add(new User("claw", 1));
        userList.add(new User("claw", 1));
        // distinct 去除重复元素
        userList.stream().distinct().forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
            }
        });
        // lambda表达式的方式
        userList.stream().distinct().forEach(System.out::println);
    }

实例(三)limit()和skip()方法测试,写到这里就觉得Lambda表达式的方便快捷了。

    @Test
    public void test6() {
        List<User> userList = new ArrayList<>();
        userList.add(new User("小蟹", 1));
        userList.add(new User("小沈", 1));
        userList.add(new User("小丁", 11));
        // limit()只展示一个元素 输出 结果: User(name=小蟹, age=1)
        userList.stream().limit(1).forEach(System.out::println);
        // skip()跳过第一个元素 输出结果: User(name=小沈, age=1)
        //User(name=小丁, age=11)
        userList.stream().skip(1).forEach(System.out::println);
    }

Stream的中间操作 - 映射

方法描述
map(Function t)接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
flatMap(Function f)接受一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有流连接成一个流

实例(一) map()方法的使用

    @Test
    public void mapTest() {
        // map(Function t)接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        // 匿名实现类写法
        list.stream().map(new Function<String, Object>() {
            @Override
            public Object apply(String s) {
                // 将列表的字符转换为大写
                return s.toUpperCase();
            }
        }).forEach(System.out::println);
        // Lambda表达式写法
        list.stream().map(e->e.toUpperCase()).forEach(System.out::println);
    }

Stream的中间操作 - 排序

(待完结)

Stream 的终止操作

(待完结)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值