前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >工作后, 你一定不能错过技术之JDK1.8的新特性

工作后, 你一定不能错过技术之JDK1.8的新特性

作者头像
时间静止不是简史
发布2020-07-27 14:30:02
5570
发布2020-07-27 14:30:02
举报
文章被收录于专栏:Java探索之路Java探索之路

在现在的企业级开发中, 随着新技术的迭代, 越来越多的公司开始使用Java8的新特性去简化开发, 因此Java8非常值得我们学习. 在学习Java8的时候, 我们需要了解一下Java8都有哪些主要的新特性

  • 函数式接口
  • Lambda表达式
  • Stream集合的流式编程
  • 新时间日期API等

而在企业开发中, 主要用到的便是Lambda表达式Stream流 , 而在下面,我们便主要的去学习这两方面的知识

Lambda表达式

Lambda表达式本质上是一个匿名函数, 可以简单理解为一个可以将代码像数据一样传递的代码 特点: 简洁, 灵活

基本语法

在写lambda表达式时, 只需关注函数和方法体即可

  • 参数: 写在小括号中
  • 方法体: 写在大括号内
  • ->: lambda表达式运算符, 分隔参数和方法体

注意:

  • lambda主要用于简化函数式接口, 而函数式接口在Java中是指: 有且仅有一个抽象方法的接口
  • 只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导

举例:

定义了三个接口, 利用lambda去实现相关功能

代码语言:javascript
复制
/**
 * Author TimePause
 * Create  2020-05-03 22:03
 */
public interface SingleReturnMultipleParameter {
    int test(int a,int b);
}

/**
 * Author TimePause
 * Create  2020-05-03 22:04
 */
public interface NoneReturnParmarter {
    void test();
}

/**
 * Author TimePause
 * Create  2020-05-03 22:26
 */
public interface SingleReturnSingleParameter {
    int test(int x);
}


/**
 * lambda表达式: 对接口进行简洁的实现
 *
 * @author TimePause
 * @create 2020-05-03 22:05
 */
public class TestLambda {
    public static void main(String[] args) {
        //测试lambda无返回值无参
        NoneReturnParmarter lambda1= ()-> {
            System.out.println("lambda-NoneReturnParmarter");
        };
        lambda1.test();
        //测试lambda单个返回值多个参数
        SingleReturnMultipleParameter lambda2=(int a,int b)->{
            return a + b;
        };
        System.out.println(lambda2.test(1, 2));
        //测试lambda单个参数单个返回值
        SingleReturnSingleParameter lambda3=(int x)->{
            return x*x;
        };
        System.out.println(lambda3.test(3));

        //测试lambda单个/多个参数省略参数类型
        SingleReturnMultipleParameter lambda5=(a,b)->{
            return a + b;
        };
        System.out.println(lambda5.test(1, 2));

        //测试lambda单个参数省略小括号(两个无法省略)
        SingleReturnSingleParameter lambda6= x->{return x*x; };

        /**
         *  注意:
         *  1.如果方法体中代码只有一行,那么大括号可以省略
         *  2.如果唯一的一行代码是返回语句,那么在大括号省略的同时, 返回值也必须省略
         */
        NoneReturnParmarter lambda7= ()-> System.out.println("lambda-NoneReturnParmarter");
        SingleReturnSingleParameter lambda8= x-> x*x;

    }
}

lambda表达式的函数引用

  • 将一个接口的实现,引用到一个已存在的方法上
  • 注意: 要引用的方法的参数和返回值要和接口中定义的一致

举例:

使用上面实现过的接口来实现引用

代码语言:javascript
复制
/**
 * Author TimePause
 * Create  2020-05-03 22:03
 */
public interface SingleReturnMultipleParameter {
    int test(int a,int b);
}

/**
 * 测试lambda表达式的引用
 *
 * @author TimePause
 * @create 2020-05-04 22:11
 */
public class TestLambda2 {
    public static void main(String[] args) {
        //测试lambda表达式的引用: 类名::方法名
        SingleReturnMultipleParameter lambda=TestLambda2::getAbs;
        lambda.test(2, 3);
        //相当于下面的方式
        SingleReturnMultipleParameter lambda1 = (a, b) -> TestLambda2.getAbs(a, b);
        System.out.println(lambda1.test(2, 3));
        //测试对非静态方法的引用
        NoneReturnParmarter lambda2=new TestLambda2()::show;
        lambda2.test();
    }

    /**
     * 引用注意:
     * 1.把私有修饰符去掉以后可在其他类中通过 类名::方法名引用其他方法
     * 2.如果是静态方法引用方式为: 类名::方法名
     *   如果是非静态方法引用方式为: 对象::方法名
     */
     static int getAbs(int a,int b){
        return a>b?a-b:b-a;
    }

    public void show(){
        System.out.println("如果是非静态方法引用方式为: 对象::方法名");
    }
}

lambda表达式对构造方法的引用

lambda在引用时,会根据接口类型不同自动适配是带参还是无参的构造方法!!!

代码语言:javascript
复制
/**
 * 测试lambda表达式对构造方法的引用
 *
 * @author TimePause
 * @create 2020-05-04 22:38
 */
public class TestLambda3 {
    public static void main(String[] args) {
        //lambda在引用时,会根据接口类型不同自动适配是带参还是无参的构造方法!!!
        PersonMake lambda1=Person::new;
        PersonMake2 lambda2 = Person::new;
    }
}
//定义一个实体类两个接口, 分别测试lambda对带参和无参方法的引用
class Person{
    String name;
    int age;

    Person(){}
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }
}

interface PersonMake{
    Person getPerson();
}

interface  PersonMake2{
    Person getPerson(String name,int age);
}

lambda表达式对get(),set()方法的引用

引用时需要根据接口进行适配

代码语言:javascript
复制
public class TestLambda3 {
    public static void main(String[] args) {
        //对get方法的引用
        getName lambda4=Person::getName;
        //对set方法的引用
        setName lambda5=new Person()::setName;
    }
}
class Person{
    String name;
    int age;
    
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
}


interface  getName{
    String get(Person p);
}

interface setName{
    void set(String name);
}

lambda简单应用1

代码语言:javascript
复制
/**
 * 测试lambda表达式实现数组的降序排序
 *
 * @author TimePause
 * @create 2020-05-05 9:24
 */
public class TestLambda4 {
    public static void main(String[] args) {
        Integer arr[] = {1, 9, 8, 3, 5, 6};
        //ctrl+f12=>查看当前类下所有方法
        Arrays.sort(arr);//这是正序
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr,(x,y)-> y-x);//这是倒序
        System.out.println(Arrays.toString(arr));
    }
}

简单应用2

将上面的TestLambda3 代码进行修改,可以测试lambda对对象数组的某个属性进行排序的情况

代码语言:javascript
复制
public class TestLambda3 {
    public static void main(String[] args) {
        Person[] persons=new Person[5];
        persons[0] = new Person("小1", 33);
        persons[1] = new Person("小2", 21);
        persons[2] = new Person("小3", 25);
        persons[3] = new Person("小4", 28);
        persons[4] = new Person("小5", 19);
        //利用lambda对其按照年龄进行正序排序
        Arrays.sort(persons,(p1,p2)->p1.getAge()-p2.getAge());
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);
        }
    }
}
//定义一个实体类两个接口, 分别测试lambda对带参和无参方法的引用
class Person{
    String name;
    int age;

    Person(){}
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Stream(集合的流式编程)

在对集合中的元素进行操作的时候, 有时候可能会用到其他结果, 在这个过程中使用集合的流式编程可以简化代码量, 将数据源中的数据读取到一个流中, 可以对这个流进行操作(删除, 过滤, 映射…)

介绍

Stream流是对集合操作的增强, 它不是一种数据结构,也不负责存储. 更像一个迭代器,且单向遍历不可循环

实现步骤

  1. 获取数据源, 读取到流中
  2. 对流中的数据进行操作(中间操作)
  3. 对流中的数据进行整合处理(最终操作)

注意:

  • 几乎所有中间操作最终操作的参数和方法都是函数式接口,
  • 因此使用集合的流式编程进行简化处理的前提是熟练使用lambda表达式

数据源的获取

数据源就是数据的来源, 从数据源中读取到流中 需要注意的是对流中数据的操作(删除, 映射, 过滤…)是不会影响数据源的数据的

代码语言:javascript
复制
/**
 * 测试Stream流式编程获取数据源
 *
 * @author TimePause
 * @create 2020-05-05 11:34
 */
public class TestStream {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        /**
         * 数据源的获取
         * 1.stream获取的数据源是串行的
         * 2.parallelStream获取的数据源是并行的, 且该方法封装了多线程对数据的操作, 效率更高
         */
        list.stream();
        list.parallelStream();

        //Stream获取数组元素
        int[] arr={1,9,8,0,2,3};
        IntStream stream = Arrays.stream(arr);
    }
}

最终操作

最终操作指的是将流中的数据整合在一起, 放入一个集合, 也可以直接对流中的数据进行遍历统计, 提取出我们想要的信息 注意: 在使用最终操作后会关闭这个流并销毁流中的数据, 如果再次使用这个已关闭的流则会出现异常

代码语言:javascript
复制
/**
 * 测试最终操作
 *
 * @author TimePause
 * @create 2020-05-05 12:09
 */
public class TestStream2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        Collections.addAll(list, 5, 10, 15, 20, 25);
        /**
         * 1.collect:
         * 通过在collect()方法内调用Collectors的其他方法,将流中的数据转化成其他类型的数据
         */
        //将最终结果转成list
        List<Integer> collect = list.stream().collect(Collectors.toList());
        System.out.println(collect);
        //将最终结果转成set
        Set<Integer> set = list.stream().collect(Collectors.toSet());
        System.out.println(set);
        //将最终结果转成map
        Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(e -> e / 5, e -> e));
        System.out.println(map);

        /**
         * 2.reduce:将流中的所有元素带入这个方法中进行运算
         * 最终的运算结果是一个optional类型的数据,需要调用get()方法获取其中的数据
         */
        int res = list.stream().reduce((e1, e2) -> e1 + e2).get();
        System.out.println(res);

        /**
         * 3.count:统计流中的数据的个数
         */
        long count = list.stream().count();
        System.out.println(count);

        /**
         * 4.foreach:遍历流中的元素
         */
        list.stream().forEach(System.out::println);

        /**
         * 5.max&min: 获取流中的最大值和最小值
         */
        Integer max = list.stream().max(Integer::compareTo).get();
        System.out.println("集合中的最大值是: "+max);
        Integer min = list.stream().min(Integer::compareTo).get();
        System.out.println("集合中的最小值是: "+min);

        /**
         * 6.allMatch & anyMatch & noneMatch=>matching
         * allMatch: 只有流中所有元素都满足匹配规则,才返回true
         * anyMatch: 只要有任意一个元素满足匹配规则,就返回true
         * noneMatch: 只有流中所有的元素都不满足匹配规则,才返回true
         */
        boolean allMatch = list.stream().allMatch(e -> e > 20);
        System.out.println(allMatch);
        boolean anyMatch = list.stream().anyMatch(e -> e > 20);
        System.out.println(anyMatch);
        boolean noneMatch = list.stream().noneMatch(e -> e %5 !=0);
        System.out.println(noneMatch);

        /**
         * 7.find: 查找流中的第一个元素
         * findFirst()和findAny()在绝大多数情况下作用是一样的, 只有在多线程的环境下才可能不同
         */
        Integer findFirst = list.parallelStream().findFirst().get();
        Integer findAny = list.parallelStream().findAny().get();
        System.out.println(findFirst +"---"+findAny);

		/**
         * 注意事项:在使用最终操作后会关闭这个流并销毁流中的数据, 如果再次使用这个已关闭的流则会出现异常
         * 异常打印如下:
         * Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
         * 	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
         * 	at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
         */
        Stream<Integer> stream = list.stream();
        long count1 = stream.count();
        System.out.println(count1);
        Integer integer = stream.findFirst().get();
        System.out.println(integer);
    }
}

中间操作

中间操作指的是对流中的数据做各式各样的处理, 中间操作可以是连续的操作, 每次操作都返回一个Stream对象, 直到最终操作的执行

代码语言:javascript
复制
/**
 * 测试Stream的中间操作(对数组,集合进行操作)
 *
 * @author TimePause
 * @create 2020-05-05 15:45
 */
public class TestStream3 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "hello", "world", "hello", "TimePause", "csdn");
        /**
         * 1.filter:过滤流中符合条件的元素
         */
        list.stream().filter(e->e.length()>5).forEach(System.out::println);

        /**
         * 2.distinct: 去除集合中重复的元素
         * 这个方法没有参数, 去重规则与hashset相同(会比较hashcode,如果hashcode相同会比较equals,equals相同则认为相同则去重)
         */
        list.stream().distinct().forEach(System.out::println);

        /**
         * 3.sorted: 排序
         * sorted():默认是按照流中的元素对应的类,实现的comparable接口中的方法进行排序
         *          也可以在()内将流中的数据按照指定的规则进行排序
         */
        list.stream().sorted().forEach(System.out::println);
        list.stream().sorted((e1,e2)->e1.length()-e2.length()).forEach(System.out::println);

        /**
         * 4.skip & limit (这两个方法一般会联合使用)
         * skip(): 跳过指定个数的元素
         * limit():截取指定个数的元素
         */
        list.stream().skip(2).forEach(System.out::println);
        list.stream().limit(2).forEach(System.out::println);
        //如何获取 hello TimePause?
        list.stream().skip(2).limit(2).forEach(System.out::println);

        /**
         * 5. map & flatMap (重要!!!)
         * map: 对流中的数据进行映射,用新的数据替换旧的数据
         * flatMap: 也是元素的映射,不过是扁平化的映射, 将容器中所有元素取出放到集合中
         */
        list.stream().map(e->e+".txt").forEach(System.out::println);

        String[] strs = {"hello world", "hello csdn", "hello TimePause", "hello csdn"};
        //数组也可以使用Stream
        Arrays.stream(strs).map(String::toCharArray).forEach(e-> System.out.println(Arrays.toString(e)));
        Arrays.stream(strs).map(e->e.split(" ")).flatMap(Arrays::stream).forEach(System.out::println); //strs->map(arrays)->flatMap(elements)

    }
}
代码语言:javascript
复制
/**
 * 测试Stream中间操作之maptoint (对对象组成的集合进行操作)
 *
 * @author TimePause
 * @create 2020-05-05 20:58
 */
public class TestStream4 {
    public static void main(String[] args) {
        ArrayList<Persons> person = new ArrayList<>();
        Collections.addAll(person,new Persons("小a",14),new Persons("小b",12),new Persons("小c",33));

        //Integer sum = person.stream().map(e -> e.getAge()).reduce((e1, e2) -> e1 + e2).get();
        //System.out.println(sum);
        /**
         * maptoInt: 将流中的数据转成int,此时这个方法的返回值不再是Stream
         *           此时这个方法的返回值不再是Stream而是IntStream
         */
        double average = person.stream().mapToInt(Persons::getAge).average().getAsDouble();//获取平均年龄
        System.out.println(average);
    }
}
class Persons{
    String name;
    int age;

    public Persons(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

综合案例

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 综合练习
 *
 * @author TimePause
 * @create 2020-05-05 21:27
 */
public class TestStream5 {
    public static void main(String[] args) {
        ArrayList<Student> studentList = new ArrayList<>();
        Collections.addAll(studentList,
                new Student("小1",59),
                new Student("小2",99),
                new Student("小3",87),
                new Student("小4",68),
                new Student("小5",71)
        );

        //1.求出所有及格的学生
        System.out.println("---------------1.求出所有及格的学生--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                forEach(System.out::println);

        //2.求出所有及格的学生的姓名
        System.out.println("---------------2.求出所有及格的学生的姓名--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                map(Student::getName).
                forEach(System.out::println);

        //3.求出所有学生的平均成绩
        System.out.println("---------------3.求出所有学生的平均成绩--------------");
        double averageScore = studentList.
                stream().
                mapToInt(Student::getScore).
                average().getAsDouble();
        System.out.println(averageScore);

        //4.求班级前三名(做成集合)???
        System.out.println("---------------4.求班级前三名(做成集合)--------------");
        List<Student> collect = studentList.
                stream().
                sorted((e1, e2) -> e2.getScore() - e1.getScore()).
                limit(3).
                collect(Collectors.toList());
        collect.forEach(System.out::println);

        //5.求班级的3-10名(做成集合)
        System.out.println("---------------5.求班级的3-10名(做成集合)--------------");
        List<Student> collect2 = studentList.
                stream().
                sorted((e1, e2) -> e2.getScore() - e1.getScore()).
                limit(10).
                skip(2).
                collect(Collectors.toList());
        collect2.forEach(System.out::println);

        //6.求所有不及格学生的平均成绩
        System.out.println("---------------6.求所有不及格学生的平均成绩--------------");
        double averageScore2 = studentList.
                stream().
                filter(e -> e.getScore() < 60).
                mapToInt(e -> e.getScore()).
                average().
                getAsDouble();
        System.out.println(averageScore2);

        //7.将及格的学生, 按照成绩降序输出所有信息
        System.out.println("---------------7.将及格的学生, 按照成绩降序输出所有信息--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                sorted((e1,e2)->e2.getScore()-e1.getScore()).
                forEach(System.out::println);


        //8.班级学生的总分=>从这里可以看出很多时候e->e.getScore()可以替换Student::getScore,都是对get()方法的引用
        System.out.println("---------------8.班级学生的总分--------------");
        int res = studentList.stream().mapToInt(Student::getScore).reduce((e1, e2) -> e1 + e2).getAsInt();
        int res2 = studentList.stream().mapToInt(e->e.getScore()).reduce((e1, e2) -> e1 + e2).getAsInt();
        int res3 = studentList.stream().mapToInt(e -> e.getScore()).sum();
        System.out.println(res);
        System.out.println(res2);
        System.out.println(res3);
    }
}

class Student{
    private String name;
    private int score;

    public Student(String name,int score ){
        this.name=name;
        this.score = score;
    }

    public String getName(){
        return name;
    }

    public int getScore(){
        return score;
    }

    @Override
    public String toString() {
        return String.format("姓名: %s, 成绩: %d", name, score);
    }
}

聪明的人能够看到本文配套学习视频哦: https://www.bilibili.com/video/BV1N7411v796?p=1

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-05-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Lambda表达式
  • Stream(集合的流式编程)
    • 数据源的获取
      • 最终操作
        • 中间操作
          • 综合案例
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档