JavaImprove--Lesson05--Arrays,对象排序,Lambda表达式,方法引用简化Lambda表达式
一.Arrays
用来操作数组的一个工具类
在Java中,没有内置的"Arrays工具类",但有一个名为java.util.Arrays的类,它包含了一些用于操作数组的静态方法。这个类主要用于数组的排序、搜索和复制
toString(类型[] arr):打印数组中的内容
int[] arr={20,30,5,8,9}; //public static String toString (类型[] arr) //返回数组中的内容 System.out.println(Arrays.toString(arr)); //[20, 30, 5, 8, 9]
//public static int[] copyOfRange(类型[], 起始索引, 结束索引) 左闭右开 //拷贝指定范围的数组,并返回一个新的数组 int[] copyArray = Arrays.copyOfRange(arr, 1, 3);//拷贝下标为 1,2号元素,左闭右开 System.out.println(Arrays.toString(copyArray)); //[30, 5]
//public static copyOf(类型[], int newLength)复制指定长度的数组 //长度大于原数组,补齐0,小于则复制0下标开始的一部分元素 int[] copyArr = Arrays.copyOf(arr, 10); System.out.println(Arrays.toString(copyArr));//[20, 30, 5, 8, 9, 0, 0, 0, 0, 0]
double[] ar = {32.0,15.4,89.3,52.1}; //public static setAll(double[] arr, IntToDoubleFunction generator) //将数组中的数据改为新数据又存进去 Arrays.setAll(ar, new IntToDoubleFunction() { //更改原始数据的方法 @Override public double applyAsDouble(int value) { //value是传入数组的下标,一次取值,分别为0,1,2..... return ar[value]*10; //将数组中的每个元素扩大 10 倍 } }); System.out.println(Arrays.toString(ar)); //[320.0, 154.0, 893.0, 521.0]
//public static void sort (类型[] arr) //对数组排序,默认升序 Arrays.sort(ar); System.out.println(Arrays.toString(ar)); //[154.0, 320.0, 521.0, 893.0]
二.对象排序
对数组的基本类型元素排序是很简单的事情,我们在初学编程语言就了解了一些排序算法,比如,冒泡,插入,选择,快速排序等算法,十大基础排序算法
简单是因为,基本数据类型的元素值单一,可以直接通过值的大小进行升序或者降序排序
而当待排序的元素是对象的时候,似乎就不那么简单了
主要是基于对象的比较有很多方式可以比较,如对象在堆空间的地址大小,对象之间的各个属性值大小,这些都是可以比较的方式,所以我们在为同一类对象进行排序的时候一定要制定对应比较规则
制定比较规则的方式有两种:
一.让对象类实现Comparable接口,重写ComparaTo方法,自己制定比较规则
二.使用Arrays类的sort方法,创建Comparator比较器接口的匿名内部类对象,再制定比较规则
实现Comparable接口
Comparable
接口是 Java 中的一个标准接口,用于定义对象之间的自然顺序。当一个类实现了 Comparable
接口后,就意味着这个类的对象可以根据某个指定的属性进行排序。
要实现 Comparable
接口,类需要实现 compareTo
方法。这个方法接受另一个对象作为参数,并返回一个整数值,表示当前对象与参数对象的大小关系。返回值的具体含义如下:
- 如果返回值为负整数,则表示当前对象小于参数对象。
- 如果返回值为零,则表示当前对象等于参数对象。
- 如果返回值为正整数,则表示当前对象大于参数对象
实现Comparable接口,重写ComparaTo方法:
public class Student implements Comparable<Student>{ private String name; private double grade; private int age; public Student(String name, double grade, int age) { this.name = name; this.grade = grade; this.age = age; } @Override public int compareTo(Student o) { return 0; } }
Comparable接口有一个泛型需要传入,直接传入当前类或接口就好了
重写的CompareTo接口就需要了解的是它的执行过程:
再启动这个方法的时候,是一个对象调用此方法和另一个对象比较,调用者就是This对象,另一个比较对象就是Student o代表的
所以两两相比较,就是this 和 o 之间的比较,比较完了,下一组又会重新刷洗 this和o所代表的对象
比如:我们需要比较年龄,年龄小的对象在前面,年龄大的在后面,注意上面的返回值约定
重写的compareTo()方法:
@Override public int compareTo(Student o) { //约定 //如果 this 大于 o 对象的属性值,则返回正整数 //如果 this 小于 o 对象的属性值,则返回负整数 //如果 this 等于 o 对象的属性值,则返回0 if(this.age>o.age){ return 1; }else if (this.age<o.age){ return -1; } return 0; }
测试类:
public static void main(String[] args) { Student[] st=new Student[4]; st[0]=new Student("李华",65,18); st[1]=new Student("王明",95,19); st[2]=new Student("张静",35,16); st[3]=new Student("李丹",85,21); Arrays.sort(st); System.out.println(Arrays.toString(st)); //[Student{name='张静', grade=35.0, age=16}, // Student{name='李华', grade=65.0, age=18}, // Student{name='王明', grade=95.0, age=19}, // Student{name='李丹', grade=85.0, age=21}] }
Arrays类的sort方法,创建Comparator匿名内部类
Comparator
是 Java 中的一个接口,用于定义对象的比较逻辑。通过实现这个接口,我们可以自定义对象的排序方式。
compare(Object o1, Object o2)
: 这个方法用于比较两个对象。返回值与 Comparable
接口中的 compareTo
方法类似,表示 o1 和 o2 的大小关系。
它也需要遵守约定和上面compareTo方法的约定是一样的
- 如果返回值为负整数,则表示当前对象小于参数对象。
- 如果返回值为零,则表示当前对象等于参数对象。
- 如果返回值为正整数,则表示当前对象大于参数对象
Arrays.sort(st, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return 0; } });
如上,compare方法的两两对象比较就是o1,o2互相比较,它的约定和上面的一样
重写 compare方法:按照成绩升序排序
//按照成绩排序 Arrays.sort(st, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { if(o1.getGrade()>o2.getGrade()){ return 1; }else if (o1.getGrade()<o2.getGrade()){ return -1; } return 0; } });
Main函数展示结果:
System.out.println(Arrays.toString(st)); //[Student{name='张静', grade=35.0, age=16}, // Student{name='李华', grade=65.0, age=18}, // Student{name='李丹', grade=85.0, age=21}, // Student{name='王明', grade=95.0, age=19}]
要想逆序也很简单,只需要把大于改小于,把小于改大于就好了:
//按照成绩排序 Arrays.sort(st, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { if(o1.getGrade()<o2.getGrade()){ return 1; }else if (o1.getGrade()>o2.getGrade()){ return -1; } return 0; } });
三.Lambda表达式
Lambda表达式是JDK8开始新增的一种语法形式;作用:用于简化匿名内部类的写法
格式:
(被重写方法的形参列表) - >{
方法体;
}
匿名内部类的写法:
抽象类
public class Animal { public static void main(String[] args) { //匿名内部类 Dog dog = new Dog() { @Override void eat() { System.out.println("Dog eat~~"); } }; } } //抽象类 abstract class Dog{ //抽象方法 abstract void eat(); }
接口类
public class Animal { public static void main(String[] args) { //匿名内部类 Cat cat = new Cat() { @Override public void eat() { System.out.println("cat eat ~~"); } }; } } //接口 interface Cat{ void eat(); }
今天我们要学习的Lambda表达式可以简化匿名内部类的写法,但是,需要注意的是不是所有的匿名内部类都可以简化写法
抽象类的匿名内部类是不能简化的
只能简化函数式接口方法的匿名内部类
简化Cat匿名内部类的写法:
public class Animal { public static void main(String[] args) { //Lambda表达式简化 Cat cat = () ->{ System.out.println("cat eat ~~"); }; } } //接口 interface Cat{ void eat(); }
什么是函数式接口呢?
有且仅有一个抽象方法的接口
同时如果我们看到一些接口上有@FunctionalInterface注解的接口就一定是函数式接口
@FunctionalInterface interface Cat{ void eat(); }
此注解会抑制接口只能有一个抽象方法
重写Arrays.sort()方法
原方法体:
Student[] st=new Student[4]; st[0]=new Student("李华",65,18); st[1]=new Student("王明",95.2,19); st[2]=new Student("张静",35,16); st[3]=new Student("李丹",95.1,21); //按照成绩排序 Arrays.sort(st, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return Double.compare(o1.getGrade(),o2.getGrade()); } }); System.out.println(Arrays.toString(st));
我们可以看看Comparator接口是不是函数式接口:
有注解,妥妥的函数式接口,所以可以使用Lambda表达式进行加简化Arrays.sort()方法
Arrays.sort(st,(Student o1, Student o2) -> { return Double.compare(o1.getGrade(),o2.getGrade()); });
上面的写法式最原始的Lambda表达式的写法,后面的Java开发又更新了很多新的简化规则,还能继续简化:
规则:
- 参数类型可以省略不写
- 只有一个参数时,括号()可以不写
- Lambda的主体方法中只有一句话时,可以省略大括号{ } 还有分号;如果这一行代码是return 语句,return 也可以不写
满足 1,2的接口:
public class Animal { public static void main(String[] args) { //Lambda表达式简化 Cat cat = vale ->{ System.out.println("cat eat ~~"); }; } } //接口 @FunctionalInterface interface Cat{ void eat(int value); }
满足1,3的接口:
//原Lambda Arrays.sort(st,(Student o1, Student o2) -> { return Double.compare(o1.getGrade(),o2.getGrade()); }); //继续简化 Arrays.sort(st,(o1, o2) -> Double.compare(o1.getGrade(),o2.getGrade()));
满足1,2,3的接口:
public class Animal { public static void main(String[] args) { //Lambda表达式简化 Cat cat = vale -> System.out.println("cat eat ~~"); } } //接口 @FunctionalInterface interface Cat{ void eat(int value); }
四.方法引用简化Lambda表达式
对于Lambda表达式我们知道,是用来简化函数式接口的匿名函数构造的
而方法引用可以进一步简化Lambda表达式
静态方法引用
类名::静态方法
如果某个Lambda表达式只是调用一个静态方法,并且前后参数一致,就可以使用静态方法的引用
原Lambda表达式:
public class StaticMethodUseDemo { public static void main(String[] args) { int[] arr={10,20,30,40,50}; ToArr ta = (array,number) -> { return ctrlArray(array,number);//对数组元素加 10 }; int[] addArray = ta.ctrlArray(arr,10); System.out.println(Arrays.toString(addArray)); //[20, 30, 40, 50, 60] } //对数据元素进行加减 public static int[] ctrlArray(int[] arr,int number){ int[] temp = new int[arr.length]; for (int i = 0; i < arr.length; i++) { temp[i]=arr[i]+number; } return temp; } } @FunctionalInterface interface ToArr{ int[] ctrlArray(int[] arr,int number); }
注意Lambda表达式部分,只调用了一个静态方法:
ToArr ta = (array,number) -> { return ctrlArray(array,number);//对数组元素加 10 };
它是满足方法引用的,因为它只调用了一个静态方法,且前后的参数形式都是一样的,所以还可以继续简化,类名::静态方法名
ToArr ta = StaticMethodUseDemo::ctrlArray;//对数组元素加 10
如上代码,以类名::方法名的方式和上面的Lambda表达式未简化的是一样的
实例方法的引用
对象名::实例方法
如果Lambda表达式中只调用一个实例方法,且前后参数形式一致,就可以使用实例方法的引用
原Lambda表达式:
public class StaticMethodUseDemo { public static void main(String[] args) { int[] arr={10,20,30,40,50}; methodClass methodClass = new methodClass(); ToArr ta = (array,number) ->{ return methodClass.ctrlArray(arr,number); }; int[] addArray = ta.ctrlArray(arr,10);//对数组元素加 10 System.out.println(Arrays.toString(addArray)); //[20, 30, 40, 50, 60] } } //方法类 class methodClass{ //对数据元素进行加减 //实例方法非静态 public int[] ctrlArray(int[] arr,int number){ int[] temp = new int[arr.length]; for (int i = 0; i < arr.length; i++) { temp[i]=arr[i]+number; } return temp; } } @FunctionalInterface interface ToArr{ int[] ctrlArray(int[] arr,int number); }
我们可以观察一下Lambda表达式的部分:
methodClass methodClass = new methodClass(); ToArr ta = (array,number) ->{ return methodClass.ctrlArray(arr,number); };
它只使用了一个实例方法,且参数前后都是一致的,所以可以使用方法引用简化Lambda表达式:
methodClass methodClass = new methodClass(); ToArr ta = methodClass::ctrlArray;
特定类型的方法引用
类型::方法
如果某个Lambda表达式只调用一个实例方法,且第一个参数作为方法的主调,后面的参数都作为该实例方法的入参,就可以使用特定类型的方法的引用
示例 (a,b,c) -> a.method(b,c ) 或者 (a,b) -> a . method(b)
原Lambda表达式:
public static void main(String[] args) { String[] names = {"andy","Bob","Alice","jack","desk"};//按名字排序 //不区分大小写 Arrays.sort(names, (o1,o2)-> { return o1.compareToIgnoreCase(o2); //此方法是String自带的,可以不区分字符大小写进行比较 }); System.out.println(Arrays.toString(names)); //[Alice, andy, Bob, desk, jack] }
我们注意看Lambda表达式:
Arrays.sort(names, (o1,o2)-> { return o1.compareToIgnoreCase(o2); //此方法是String自带的,可以不区分字符大小写进行比较 });
符合Lambda表达式只有一个实例方法,并且第一个参数为主调,后面的参数作为方法入参,可以继续简化:
Arrays.sort(names, String::compareToIgnoreCase); //此方法是String自带的,可以不区分字符大小写进行比较
构造器引用
类名::new
当某个Lambda表达式正在创建对象的时候,且前后参数一致,就可以使用构造器引用
开发中是使用的比较少,因为没有人会写一个函数式接口目的只是为了创建一个对象
public class StaticMethodUseDemo { public static void main(String[] args) { //创建一个methodClass对象 ToArr ta = () ->{ return new methodClass(); }; methodClass creat = ta.creat(); } } //方法类 class methodClass{ } @FunctionalInterface interface ToArr{ methodClass creat(); }
主要看Lambda表达式:
ToArr ta = () ->{ return new methodClass(); };
Lambda的主体是创建一个对象,且参数都一致(没有参数),所以可以使用方法引用继续优化:
ToArr ta = methodClass::new;
总结:
Lambda表达式是蒸滴C,能简化成这样,真是没谁了