Arrays和Lambda表达式
Arrays类
Arrays类是操作数组的工具类,其构造方法被私有化,不允许从外部创建对象,所有的方法都是静态的(构造方法被私有化,成员方法为静态的)
- 常用方法
package com.an.a;
import java.util.Arrays;
public class ArraysTest {
public static void main(String[] args) {
/*
常见方法:
public static String toString(数组) 把数组拼接成一个字符串
public static int binarySerach(数组,查找的元素) 二分查找数组中的元素
public static int[] copyOf(原数组,新数组长度) 拷贝数组
public static int[] copyRange(原数组,起始索引,结束索引)拷贝数组(指定范围)
public static void fill(数组,元素) 填充数组
public static void sort(数组) 按照默认的方式进行数组排序
public static void sort(数组,排序规则) 按照指定的规则排序
*/
//1.将数组转化为字符串
System.out.println("----------------------------toString--------------------------------------------------");
int a[]={1,3,4,5,6,9};
String s = Arrays.toString(a);//将数组转化为字符串
System.out.println(s);//[1, 3, 4, 5, 6, 9]
//2.二分查找数组中的元素
//二分查找细节:
//1.进行查找之前数组中的元素必须是有序的(java这里必须是升序的)(这里java写的方法在排序之前自己都实现了)
//2.如果要查询的元素在数组中存在,返回该元素的真实索引
//如果要查询的元素不存在,返回的是 -插入点(即如果将该元素放进数组中有序后的索引)-1
//解释:为什么不存在要这样返回:如果我们所查询的元素不存在但是它的插入点在0索引-0还是0就会带来歧义 -1可以避免这个问题
System.out.println("-------------------------------------binarySerach------------------------------------------------");
int b[]={2,3,4,5,9,11};
System.out.println(Arrays.binarySearch(b,2));//0
System.out.println(Arrays.binarySearch(b,11));//5
System.out.println(Arrays.binarySearch(b,12));//-7
//数组拷贝
System.out.println("-----------------------------------------copyOf------------------------------------------");
/*
参数1:老数组
参数2:新数组的长度
方法底层会根据第二个参数来创建数组
如果新数组的长度小于老数组的长度 会部分拷贝
如果新数组的长度大于老数组的长度 会完全拷贝 并在多余的位置补上默认值
如果新数组的长度等于老数组的长度 会完全拷贝
*/
int c[]={1,2,3,4,5,6,7};
System.out.println(Arrays.toString(Arrays.copyOf(c, 4)));//[1, 2, 3, 4]
System.out.println(Arrays.toString(Arrays.copyOf(c, 10)));//[1, 2, 3, 4, 5, 6, 7, 0, 0, 0]
System.out.println(Arrays.toString(Arrays.copyOf(c, 7)));//[1, 2, 3, 4, 5, 6, 7]
//指定返回的拷贝数组
//包头不包尾
System.out.println("---------------------------copyRange-----------------------------------");
int d[]={1,2,3,4,5};
System.out.println(Arrays.toString(Arrays.copyOfRange(d, 0, 4)));//[1, 2, 3, 4](包头不包尾)
//fill填充数组
System.out.println("--------------------------------fill----------------------------------------");
int e[]=new int[10];
Arrays.fill(e,6);//用6来填充数组
System.out.println(Arrays.toString(e));//[6, 6, 6, 6, 6, 6, 6, 6, 6, 6]
//sort排序:默认情况下给基本数据类型按照升序排序(底层使用快排)
System.out.println("--------------------------------------------sort---------------------------");
int w[]={3,1,6,2,5,10};
Arrays.sort(w);
System.out.println(Arrays.toString(w));//[1, 2, 3, 5, 6, 10]
}
}
我们知道sort方法是按照默认的比较规则进行比较的,默认的规则是按照升序排序的,如果此时我们需要降序排序,怎么办呢?sort的一个重载方法可以解决这个问题
package com.an.a;
import java.util.Arrays;
import java.util.Comparator;
public class ArraysTest {
public static void main(String[] args) {
/*
public static void sort(数组,排序规则) 按照指定的规则排序
参数一:要排序的数组
参数二:排序的规则
只能给引用数据类型的数组排序,
如果数组是基本数据类型需要变成其对应的包装类
*/
Integer a[]={2,1,4,3,9,6,7};//这里注意为包装类
Arrays.sort(a, new Comparator<Integer>() {
//第二个参数是一个接口,所以我们在调用方法的时候,需要传递这个接口的实现类对象,作为排序的规则
//但是这个实现的类,我们只需要使用一次,所以没有必要取单独编写一个类,直接使用匿名内部类的方式就可以了
/*
底层原理:
1.利用插入排序+二分查找来排序的
默认把0索引的数据看成是有序的序列,把1索引到最后认为是无序的序列
遍历无序序列得到里面的每一个元素,假设当前遍历得到的元素是A元素
把A往有序序列中进行插入,在插入的时候,是利用二分查找确定A的插入点
拿着A跟插入点的元素进行比较,比较的规则就是compare方法的方法体
如果方法的返回值负数,拿着A继续跟前面的数据进行比较
如果方法的返回值正数,拿着A继续跟后面的数据进行比较
如果方法的返回值0,拿着A继续跟后面的数据进行比较
直到能确定A的位置为止
compare方法的形式参数:
参数1:o1 表示在无序序列中遍历得到的每一个元素
参数2:o2 有序序列中的元素
返回值:
负数:表示当前要插入的元素是小的 ,放在前面
正数:表示当前要插入的元素是大的 ,放在后面
0:表示当前插入元素跟现在元素一样大,也会发在后面
*/
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("-----------------------------");
System.out.println("o1:"+o1);
System.out.println("o2:"+o2);
return o2-o1;
//结合程序运行结果和插入排序+二分查找 来理解排序的过程
//简单理解:o1-o2:升序排列
//o2-o1:降序排列
}
});
//打印排序结果
System.out.println(Arrays.toString(a));//[9, 7, 6, 4, 3, 2, 1]
}
}
Lambda表达式
- 体验Lambda表达式
对以上排序的代码进行简化处理
Lambda表达式简化匿名内部类的书写
package com.an.a;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaTest {
public static void main(String[] args) {
Integer a[]={2,1,4,9,7,3};
/*Arrays.sort(a, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});[9, 7, 4, 3, 2, 1]*/
//对以上排序的过程进行简化
Arrays.sort(a,(Integer o1,Integer o2)->{
return o2-o1;
}
);//[9, 7, 4, 3, 2, 1]
System.out.println(Arrays.toString(a));
}
}
- Lambda表达式的格式
不关心谁去做,只关系方法体
如果满足这个要求,加上这个注解后不会报错,如果不满足这个要求加上这个注解就会报错
package com.an.a;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaTest {
public static void main(String[] args) {
//测试Lambda表达式是基本用法
/*
Lambda注意点:
1.Lambda表达式可以用来简化匿名内部类的书写
2.Lambda表达式只能简化函数式接口的匿名内部类的写法
2.函数式接口:
有且仅有一个抽象方法的接口叫函数式接口,该接口可以加@FunctionalInterface进行注解
*/
/*
利用匿名内部类的形式去调用下面的方法:
调用一个方法的时候,如果该方法的形参是一个接口,那么我们要传递这个接口的实现类对象
如果实现类对象只要用到一次,就可以用匿名内部类的形式进行书写
*/
/* method(//new Swim() { 多删除了一个{后面要删除一个}
@Override
public void swimming//() {
System.out.println("hello world");//hello world
}
});*/
//用Lambda表达式进行改写
method(()-> {
System.out.println("hello world");//hello world
}
);
}
public static void method(Swim s){
s.swimming();
}
@FunctionalInterface //函数式接口注解(如果不符合函数式接口的要求注解处将会报错)
interface Swim{
public abstract void swimming();
}
}
Lambda表达式的省略写法
method(()-> {
System.out.println("hello world");//hello world
}
);
}
函数式的编程重要的是函数体,而且一定要是函数式的接口,通过以上的写法,如果我们不是函数式的接口,则说明我们的接口里面的抽象方法不止一个,这是我们所指示的方法体 System.out.println("hello world");//hello world就不知道是哪个抽象函数的重写方法体了,这就造成了混乱
Lambda表达式的省略规则
package com.an.a;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaTest2 {
public static void main(String[] args) {
/*
Lambda表达式省略规则:(都是对覆写的方法进行优化的)
1.参数类型可以省略不写(需要同时省略)
2.如果只有一个参数,参数类型可以省略,同时()也可以省略
3.如果Lambda表达是方法体只有一行,大括号 分号,return可以省略不写,但是需要同时省略
*/
//以Arrays类中的sort方法为例
Integer[]arr = {3,2,5,4,9,8};
//不使用Lambda表达式
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
//Lambda表达式的完整写法
Arrays.sort(arr,(Integer o1, Integer o2)-> {
return o2-o1;
}
);
//Lambda表达式的省略写法
Arrays.sort(arr,( o1, o2)-> o2-o1); //符合规则1 和规则3
}
}
Lambda表达式的练习
package com.an.a;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaTest {
public static void main(String[] args) {
/*
定义一个数组储存一些字符串,利用 Arrays中的sort进行排序
要求:
按照字符串的长度进行排序,短的在前面,长的在后面
*/
String[]arr={"cc","c","cccc","ccc"};
//1.不使用Lambda
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();//按照字符串长度的升序进行排序
}
});
//2.Lambda表达式的完整模式
Arrays.sort(arr, (String o1, String o2)-> {
return o1.length()-o2.length();//按照字符串长度的升序进行排序
}
);
//3.Lambda表达式的简化版本
/*
简化规则:
1.小括号中:参数的数据类型都可以省略,如果只有一个参数参数的小括号也可以省略
2.大括号:如果方法体只有一行:大括号 分号 return可以省略(必须同时省略)
*/
Arrays.sort(arr, ( o1, o2)-> o1.length()-o2.length());
}
}
5个算法的小练习
第一题(和Lambda相关)
package com.an.a;
import java.util.Arrays;
import java.util.Comparator;
/*String str1="a";
String str2="b";
int result =str1.compareTo(str2);//实际是用str1-str2 即按照升序进行的
System.out.println(result);
*/
public class LambdaPractice {
public static void main(String[] args) {
/*
定义数组并储存一些女朋友的对象,利用Arrays中的sort方法进行排序
要求:
1.女朋友属性有姓名 年龄 身高
2.按照年龄的大小进行排序,年龄一样按照身高进行排序,姓名一样按照姓名的字母进行排序
(姓名中不要有中文或特殊字符,涉及后面的知识)
*/
//1.创建3个女朋友的对象
GirlFriends grirl1 =new GirlFriends("angelababy",23,167);
GirlFriends grirl2 =new GirlFriends("xiaoshishi",23,163);
GirlFriends grirl3 =new GirlFriends("xiaodandan",22,153);
GirlFriends[]arr ={grirl1,grirl2,grirl3};//此时的数组中放匿名对象也是可以的
//2.利用sort方法进行排序
/*Arrays.sort(arr, new Comparator<GirlFriends>() {
//按照年龄的大小进行排序,年龄一样按照身高进行排序,姓名一样按照姓名的字母进行排序
@Override
public int compare(GirlFriends o1, GirlFriends o2) {
double temp = o1.getAge() - o2.getAge();
temp=temp==0?o1.getHeight()-o2.getHeight():temp;
temp=temp==0?o1.getName().compareTo(o2.getName()):temp;
//return (int)temp;// 这里如果直接返回temp 如果temp=0.1 返回的化将会强制转为0 导致结果错误
if(temp>0){
return 1;
}else if(temp<0){
return -1;
}else {
return 0;
}
}
});*/
//2.使用Lambda进行简写
Arrays.sort(arr, ( o1, o2)-> {
double temp = o1.getAge() - o2.getAge();
temp=temp==0?o1.getHeight()-o2.getHeight():temp;
temp=temp==0?o1.getName().compareTo(o2.getName()):temp;
//return (int)temp;// 这里如果直接返回temp 如果temp=0.1 返回的化将会强制转为0 导致结果错误
if(temp>0){
return 1;
}else if(temp<0){
return -1;
}else {
return 0;
}
}
);
}
}
为什么在比较姓名的时候要调用CompareTo方法
compareTo方法是Comparable接口中的抽象方法,在所有的包装类和String类中都实现了该接口,并且覆写了compareTo方法,可以使用默认的规则对该类型的数据进行比较操作。我们在比较自己写的GirlFrinds类中的各项属性进行比较,基础数据类型只需要相减得出0 1 -1 即可,但是姓名是String类型,其默认的比较规则不是简单的升序,而是按照Ascii码的顺序进行排序,所有需要调用String类的compareTo方法进行排序,得出0 1 -1