Java数据结构——反射枚举及Lambda

目录

1.反射

1.1定义

1.2用途(了解)

1.3反射基本信息

1.4反射相关的类(重要)

1.4.1Class类(反射机制的起源)

1.Class类中的相关方法(方法的使用在后面的示例当中)

 1.4.2反射示例

1.获得Claas对象的三种方式

2.反射的使用

1.5反射的优点和缺点

1.6重点总结

2.枚举的使用

2.1背景及定义

2.2使用

2.3枚举优缺点

3.枚举和反射

3.1枚举是否可以通过反射,拿到实例对象呢?

3.2总结

4.Lambda表达式

4.1背景

4.1.1Lambda表达式的语法

4.1.2函数式接口

4.2Lambda表达式的基本原理

4.3变量捕获

4.3.1匿名内部类

4.3.2匿名内部类的变量捕获

4.3.3 Lambda的变量捕获

 4.4 Lambda的变量的捕获

4.4.1Collection接口

4.4.2List接口

4.4.3Map接口

4.5总结


1.反射

1.1定义

        Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。

1.2用途(了解)

1、在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
2、反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。

1.3反射基本信息

        Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类。

1.4反射相关的类(重要)

1.4.1Class类(反射机制的起源)

Class代表类的实体,在运行的Java应用程序中表示类和接口。

Java文件被编译后,生成了.class 文件,JVM此时就要去解读.class文件,被编译后的java文件即.class也被JVM解析为一个对象,这个对象就是java.lang.Class。这样当程序在运行时,每个Java文件就最终变成了Class类对象的一个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。

     

1.Class类中的相关方法(方法的使用在后面的示例当中)
  • (重要)常用获得类的相关方法

  • (重要)获得类中属性的相关方法(以下方法返回值为Field相关)

  • (了解)获得类中注解相关的方法

  • (重要)获得类中构造器相关的方法(以下方法返回值Constructor相关)

  • (重要)获得类中方法相关的方法(以下方法返回值为Method相关)

 1.4.2反射示例

1.获得Claas对象的三种方式

在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射的目的,即:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到,那么,我们就可以修改部分类型信息。

第一种:使用Class.forName("类的全路径名");静态方法。

前提:已明确类的全路径名。

第二种:使用.class方法。

说明:仅适合在编译前就已经明确要操作的Class

第三种: 使用类对象的getClass()方法

示例:

class  Student{
    private String name = "bit";
    //公有属性age
    public int age = 18;
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }
    private Student(String name, int age){
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    private void eat(){
        System.out.println("i am eat");
    }

    public void sleep(){
        System.out.println("i am pig");
    }

    private void function(String str){
        System.out.println(str);
    }

    public String toString(){
        return "Student{"+"name="+name+"+age="+ age+'}';
    }
}
public class Testdemo {
    public static void main(String[] args) throws ClassNotFoundException  {
            /*
    通过getClass获取Class对象
     */
        Student s1 = new Student();
        Class c1 = s1.getClass();
        /*
        直接通过类名.class的方式得到,该方法最为安全可靠,程序性能更高,
        这说明任何一个类都有一个隐含的静态成员变量.class
         */
        Class c2 = Student.class;
        /*
        通过Class对象的forName()静态方法来获取,用的最多,
        但可能抛出ClassNotFoundException异常
         */
        Class c3 = null;
            //注意这里是类的全路径,如果有包需要加包的路径
            c3 = Class.forName("Student");

        //一个类在JVM中只有一个Class实例,即我们对上面获取的
        //c1,c2,c3进行equals比较,发现都是true
        System.out.println(c1.equals(c2));
        System.out.println(c1.equals(c3));
        System.out.println(c2.equals(c3));
    }
}

2.反射的使用

接下俩我们开始使用反射,我们依旧反射上面的Student类,把反射的逻辑写到另外的类当中进行理解。

示例(详细注解):

class Student{
    private String name = "bit";
    //公有属性age
    public int age = 18;
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }
    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }private void eat(){
        System.out.println("i am eat");
    }
    public void sleep(){
        System.out.println("i am pig");
    }
    private void function(String str) {
        System.out.println(str);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class ReflectClassDemo {
    //创建对象
    public static void reflectNewInstance(){
        try{
            //创建Class类对象
            Class<?> classStudent =Class.forName("Student");
           //创建Student类的实例,利用Class类的newInstance函数创建Student实例,返回的类型是Objecct类型的
            Object objectStudent = classStudent.newInstance();
            //newInstance()返回值的类型是Object,所以要强转Student
            Student student = (Student) objectStudent;
            System.out.println("获得学生对象:"+student);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    //反射私有的构造方法,屏蔽内容为获得公有的构造方法
    public static void reflectPrivateConstructor(){
        try{
            //创建Class类对象
            Class<?> classStudent = Class.forName("Student");
            //获取该类的构造方法,注意传入对应的参数
            Constructor<?> declareConstructStudent = classStudent.getDeclaredConstructor(String.class,int.class);
            //Construct<?> declaredConstructorStudent = classStudent.getConstructor();
            //设置为true后可修改访问权限
            declareConstructStudent.setAccessible(true);
            Object objectStudent = declareConstructStudent.newInstance("李科",16);
            //Object objectStudent = declaredconstructorStudent.newInstance();
            Student student = (Student) objectStudent;
            System.out.println("获得私有构造函数且修改姓名和年龄:"+student);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    //反射私有属性
    public static void reflectPrivateField(){
        try{
            //创建Class对象
            Class<?> classStudent = Class.forName("Student");
            //获取类中属性的方法,返回值为Field类相关的
            Field field = classStudent.getDeclaredField("name");
            //设置为true后可修改访问的权限,Field类的函数
            field.setAccessible(true);
            //获取该Student类的实例
            Object objectStudent = classStudent.newInstance();
            //为什么不用 Student student = new Student();因为Class类对象来调用其构造器,
            //而不是直接调用该类的构造函数来创建对象
            Student student =(Student) objectStudent;
            //设置属性
            field.set(student,"小明");
            String name =(String) field.get(student);
            System.out.println("反射私有属性修改了name:"+name);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    //反射私有方法
    public static void reflectPrivateMethod(){
        try{
            //获得Class类对象
            Class<?> classStudent = Class.forName("Student");
            //获得类中的某个方法,返回值为Method,函数名加参数类型
            Method methodStudent = classStudent.getDeclaredMethod("function", String.class);
            System.out.println("私有方法的方法名为:"+methodStudent.getName());
            //私有的一般都要加
            methodStudent.setAccessible(true);
            //创建student类型的实例
            Object objectStudent = (Student)classStudent.newInstance();
            //调用了student类的function方法
            methodStudent.invoke(objectStudent,"我是给私有的function函数传的参数");

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        //reflectNewInstance();
        //reflectPrivateConstructor();
        //reflectPrivateField();
        reflectPrivateMethod();
    }
}

1.5反射的优点和缺点

优点:

  1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。
  2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力。
  3. 反射已经运用在了很多流行框架:Struts、Hibernate、Spring等等;

缺点:

  1. 使用反射会有效率问题。会导致程序效率降低(比如调用各种函数)。
  2. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。

1.6重点总结

  1. 反射的意义
  2. 反射重要的几个类:Class类,Field类、Method类、Constructor类
  3. 学会合理利用反射,一定要在安全环境下使用。

2.枚举的使用

2.1背景及定义

枚举是在JDK1.5以后引入的。主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式 :

public static int final RED =1;

public static int final GREEN = 2;

public static int final BLACK = 3;

但是常量举例有不好的地方,例如:可能碰巧有个数字1,但是他有可能误会为是RED,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,枚举类型。而不是普通的整型1。

public enum TestEnum{

        RED,BLACK,GREEN;

}

优点:将常量组织起来统一进行管理。

场景:错误状态码,消息类型,颜色的划分,状态机等等....

本质:是java.lang.Enum的子类,也就是说,自己写的枚举类,就算没有显示继承Enum,但是其默认继承了这个类。

2.2使用

1.switch语句

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum testEnum = TestEnum.BLACK;
        switch(testEnum){
            case RED:
                System.out.println("red");
                break;
            case BLACK:
                System.out.println("black");
                break;
            case WHITE:
                System.out.println("white");
                break;
            case GREEN:
                System.out.println("green");
                break;
            default:
                break;
        }
    }
}

2.常用方法

Enum类的常用方法

values(),ordinal(),valueOf()示例:

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;

    public static void main(String[] args) {
        //以数组的形式返回枚举类型的所有成员
        TestEnum[] testEnums = TestEnum.values();
        for (int i = 0;i<testEnums.length;i++){
            //ordinal获取枚举成员的索引位置
            System.out.println(testEnums[i]+" "+testEnums[i].ordinal());
        }
        System.out.println("========================");
        //将普通字符串转换成枚举实例,这个方法接收一个字符串参数,
//该字符串参数必须是枚举实例的名称不然会报错
        //(不区分大小写),然后返回匹配的枚举实例。
        System.out.println(TestEnum.valueOf("GREEN"));
    }
}

 运行结果:

compareTo()示例:

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        //拿到枚举实例BLACK
        TestEnum testEnum = TestEnum.BLACK;
        //拿到枚举实例RED
        TestEnum testEnum1 = TestEnum.RED;
        //compareTo()比较枚举成员在定义时的顺序
        System.out.println(testEnum.compareTo(testEnum1));
        System.out.println(BLACK.compareTo(RED));
        System.out.println(RED.compareTo(BLACK));
    }
}

运行结果:

刚刚说过。在java当中枚举实际上就是一个类,所以我们在定义枚举的时候,还可以这样定义和使用枚举。

重要:枚举的构造方法默认是私有的

 示例:

public enum TestEnum {
        RED("red",1),BLACK("BLACK",2),WHITE("white",3),GREEN("green",4);
        private String name;
        private int key;
        /*
        1.当枚举对象有参数后,需要提供相应的构造函数
        2.枚举的构造函数默认是私有的,这个一定要记住
         */
        private TestEnum(String name ,int key){
                this.name = name;
                this.key = key;
        }
        //通过key,获得某个枚举成员
        public static TestEnum getEnumKey(int key){
                for(TestEnum t:TestEnum.values()){
                        if(t.key == key){
                                return t;
                        }
                }
                return null;
        }

        public static void main(String[] args) {
                System.out.println(getEnumKey(2));
        }
}

2.3枚举优缺点

优点:

  1. 枚举常量更简单安全。
  2. 枚举具有内置方法,代码更优雅。

缺点:

        不可继承,无法扩展。


3.枚举和反射

3.1枚举是否可以通过反射,拿到实例对象呢?

我们刚刚在反射里面看到了,任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?接下来,我们来实验一下:

同样利用上述提供的枚举类来进行举例:

public enum TestEnum {
        RED("red",1),BLACK("BLACK",2),WHITE("white",3),GREEN("green",4);
        private String name;
        private int key;
        /*
        1.当枚举对象有参数后,需要提供相应的构造函数
        2.枚举的构造函数默认是私有的,这个一定要记住
         */
        private TestEnum(String name ,int key){
                this.name = name;
                this.key = key;
        }
        //通过key,获得某个枚举成员
        public static TestEnum getEnumKey(int key){
                for(TestEnum t:TestEnum.values()){
                        if(t.key == key){
                                return t;
                        }
                }
                return null;
        }
        public static void reflectPrivateConstructor(){
                try{
                        Class<?> classStduent = Class.forName("TestEnum");
                        //注意传入对应的参数,获得对应的构造方法来构造对象,当前
                        //枚举类是提供了两个参数分别是String和int。
                        Constructor<?> declaredConstructorStudent =
                                classStduent.getDeclaredConstructor(String.class,int.class);
                        //设置为true后可修改访问权限
                        declaredConstructorStudent.setAccessible(true);
                        Object objectStudent = declaredConstructorStudent.newInstance("绿色",666);
                        TestEnum testEnum = (TestEnum) objectStudent;
                        System.out.println("获得枚举的私有构造函数:"+testEnum);
                }catch (Exception ex){
                        ex.printStackTrace();
                }
        }

        public static void main(String[] args) {
                //System.out.println(getEnumKey(2));
                reflectPrivateConstructor();
        }
}

输出结果:

 是的,报错了,你不能通过反射获取枚举类的实例。

3.2总结

  1. 枚举本身就是一个类,其构造方法默认认为私有,且都是默认继承java.lang.Enum。
  2. 枚举可以避免反射和序列化问题。
  3. 枚举的优点和缺点。

4.Lambda表达式

4.1背景

Lambda表达式是javaSE8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样,他提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。Lambda表达式可以看作是一个匿名函数,基于数学中\AAλ演算得名,也可称闭包(Closure)。

4.1.1Lambda表达式的语法

基本语法(parameter)->expression或(parameter)->{statement;}

Lambda表达式由三部分组成:

  1. parameter:类似方法中形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
  2. ->:可以理解为“被用于”的意思。
  3. 方法体:可以是表达式也可以是代码快,是函数式接口里方法的实现。代码块可返回一个值或者什么都不返回,这里的代码块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回。

//1.不需要参数,返回值为2

()->2

//2.接收一个参数(数字类型),返回其2倍的值

x->2*x

//3.接收2个参数(数字),并返回他们的和

(x,y) ->x+y

//4.接收2个int型整数,返回他们的乘积

(int x, int y)->x*y

//5.接收一个string对象,并在控制台打印,不返回任何值(看起来像是返回void)

(String s)->System.out.print(s)

4.1.2函数式接口

要了解Lambda表达式,首先要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法。

注意:

如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口,那么该接口就是一个函数式接口。

如果我们在某个接口上声明了@FunctionalInterfaceface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译器就会报错。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解,加上就会自动进行检测的。

当接口只有一个抽象方法时我们就可以利用Lambda表达式去实现这个抽象方法。

定义方式:

@FunctionalInterface

interface NoParaterNoreturn{

        //注意:只能有一个方法

        void test();

}

但是这种方式也是可以的:

@FunctionalInterface

interface NoParaterNoReturn{

        void test();

        default void test2(){

                System.out.println("JDK1.87特性,default默认方法可以有具体的实现");

        }

}

4.2Lambda表达式的基本原理

首先,我们实现准备好几个接口:

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
        void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
        void test(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
        void test(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
        int test();
}

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
        int test(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
        int test(int a,int b);
}

我们在上面提到过,Lambda表达式本质是一个匿名函数,函数的方法是:返回值 方法名 参数列表 方法体。然而在Lambda表达式中我们只关心:参数列表 方法体。

具体使用见以下代码:

public class TestDemo {
    public static void main(String[] args) {
        NoParameterNoRetrun noParameterNoRetrun = ()->{
            System.out.println("无参数返回值");
        };
        //Lambda表达式实现方法体后,调用test函数
        noParameterNoRetrun.test();
        MoreParameterNoreturn moreParameterNoreturn = (int a,int b)->{
            System.out.println("无返回值多个参数:"+a+" "+b);
        };
        moreParameterNoreturn.test(20,30);
        NoParameterReturn noParameterReturn =()->{
            System.out.println("有返回值无参数!");
            return 40;
        };
        //接收函数的返回值
        int ret = noParameterReturn.test();
        System.out.println(ret)
        ;
        OneParameterReturn oneParameterReturn = (int a)->{
            System.out.println("有返回值有参数");
            return  a;
        };
        ret = oneParameterReturn.test(50);
        System.out.println(ret);

        MoreParameterReturn moreParameterReturn = (int a,int b)->{
            System.out.println("有返回值多个参数!");
            return a+b;
        };
        ret = moreParameterReturn.test(60,70);
        System.out.println(ret);
    }
}

语法精简:

  1. 参数类型可以省略,如果需要省略,每个参数类型都要省略。
  2. 参数的小括号里面只有一个参数,那么小括号可以省略。
  3. 如果方法体当中只有一句代码,那么大括号可以省略。
  4. 如果方法体只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字。
   public static void main(String[] args) {
        MoreParameterNoreturn moreParameterNoreturn =(a,b)->{
            System.out.println("无返回值多个参数,省略参数类型:"+a+" "+b);
        };
        moreParameterNoreturn.test(20,30);

        OneParameterNoreturn oneParameterNoreturn = a->{
            System.out.println("无参数一个返回值,小括号可以省略:"+a);
        };
        oneParameterNoreturn.test(10);

        NoParameterNoRetrun noParameterNoRetrun = ()-> System.out.println("无参数" +
                "无返回值,方法体中只有一行代码");
        noParameterNoRetrun.test();
        //方法体中只有一条语句,且是return语句
        NoParameterReturn noParameterReturn =() ->40;
        int ret = noParameterReturn.test();
        System.out.println(ret);
    }

4.3变量捕获

概念:允许内部函数记住并访问外部函数的变量(如果你在一个函数或代码块内部使用了在外部定义的变量,我们就说这个变量被“捕获”了。)

Lambda表达式中存在变量捕获,了解了变量捕获之后,我们才能更好地理解Lambda表达式的作用域,java当中的匿名内部类中,会存在变量捕获。

4.3.1匿名内部类

匿名内部类就是没有名字的内部类。我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用就好,简单的看看匿名内部类的使用就好了。

class Test{
    public void func(){
        System.out.println("func()");
    }
}
public class Testdemo {
    public static void main(String[] args) {
        new Test(){
            @Override
          public void func(){
              System.out.println("我是匿名内部类,而且重写fucn这个方法");
          }
        }.func();
    }
}

运行结果:

上述代码当中的main函数当中,我们看到的就是一个匿名内部类的简单使用。

4.3.2匿名内部类的变量捕获

class Test{
    public void func(){
        System.out.println("func()");
    }
}
public class TestDemo {
    public static void main(String[] args){
        int a =100;
        new Test(){
            public void func(){
                System.out.println("我是内部类,且重写了func这个方法");
                System.out.println("我是捕获到变量 a=="+a+
                        "我是一个常量,或者是一个没有改变过值的变量!");
            }
        }.func();
    }
}

运行结果:

在上述代码当中的变量a就是,捕获的变量。这个变量要么是被final修饰,如果不是被final修饰的,即你要保证在使用之前,没有修改。如下代码就是错误的代码。

public class TestDemo {
    public static void main(String[] args) {
        int a = 100;
        new Test(){
        @Override
            public void func() {
                a = 99;
                System.out.println("我是内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量 a == "+a
            +" 我是一个常量,或者是一个没有改变过值的变量!");
            }
        };
    }
}

该代码会编译报错。

4.3.3 Lambda的变量捕获

在Lambda当中也可以进行变量的捕获,具体我们看一下代码。

@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}
public static void main(String[] args) {
    int a = 10;
    NoParameterNoReturn noParameterNoReturn = ()->{
    // a = 99; error
        System.out.println("捕获变量:"+a);
    };
    noParameterNoReturn.test();
}

结果:

a

 4.4 Lambda的变量的捕获

为了能够让Lambda和java的集合类更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接。

下面的这些接口的方法,可以利用lambda表达式去实现,

 注意:Collection的forEach()方法是从接口java.lang.Iterable拿过来。

4.4.1Collection接口

foreach()方法演示

该方法在接口Iterable当中,原型如下:

default void foreach(Consumer<? super T> action){
    Object.requireNonNull(action);
       for(T t :this){
           action.accept(t);
    }
} 

该方法表示:对容器中的每个元素执行action指定动作。

 

public class Solution {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("haha");
        list.add("Hello");
        list.add("Lambda");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                //简单遍历集合整的元素
                System.out.println(s+" ");
            }
        });
    }
}

输出结果:

我们可以利用Lambda表达式改一下上面的代码:

public class Solution {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("haha");
        list.add("Hello");
        list.add("Lambda");
        //表示调用一个,不带有参数的方法,
        // 其执行花括号内的语句,为原来的函数体内容
        list.forEach(s->{
            System.out.println(s);
        });
    }
}

输出结果一样。

4.4.2List接口

sort()方法的演示

sort方法源码:该方法根据c指定的比较规则对容器元素进行排序。

public void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, size, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

使用示例:

public class Solution {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("haha");
        list.add("Hello");
        list.add("Lambda");
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        });
        System.out.println(list);
    }
}

输出结果:

修改为Lambda表达式:

public class Solution {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("haha");
        list.add("Hello");
        list.add("Lambda");
        //调用带有2个参数的方法,且返回长度的差值
        list.sort((s1,s2)->s1.length()-s2.length());
        System.out.println(list);
    }
}

输出结果和上面的一样。

4.4.3Map接口

HashMap的forEach()

该方法的原型如下:

default void forEach(BiConsumer<? super K, ? super V> action) {
    Objects.requireNonNull(action);
    for (Map.Entry<K, V> entry : entrySet()) {
        K k;
        V v;
        try {
            k = entry.getKey();
            v = entry.getValue();
        } catch(IllegalStateException ise) {
    // this usually means the entry is no longer in the map.
            throw new ConcurrentModificationException(ise);
        }
        action.accept(k, v);
    }
}

作用是对Map中的每个映射执行action指定操作。

利用匿名内部类代码示例:

public class Solution {
    public static void main(String[] args) {
        HashMap<Integer,String> map =new HashMap<>();
        map.put(1,"hello");
        map.put(2,"lihua");
        map.put(3,"hello");
        map.put(4,"lambda");
        map.forEach(new BiConsumer<Integer, String>() {
            @Override
            public void accept(Integer k, String v) {
                System.out.println(k+"="+v);
            }
        });
    }
}

输出结果:

4.5总结

Lambda表达式的优点明显,在代码层次来说,使代码变得非常的简洁。缺点也很明显,代码不宜读。

  • 优点:
  1. 代码简洁,开发迅速
  2. 方便函数式编程
  3. 非常容易进行并行计算
  4. java引入Lambda,改善了集合操作

  • 缺点:
  1. 代码可读性变差
  2. 在非并行计算中,很多计算未必有传统的for性能高
  3. 不容易进行调试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小凌不是林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值