欢迎浏览我的博客 获取更多精彩文章
剖析Arrays
- Arrays类常用函数
- Arrays类部分方法的设计
Arrays类常用函数
在数组的操作中,Arrays作为一个工具类,是十分好用的,我们就先来看一下,Arrays有哪些经常会用到的方法
toString(*)
这个toString方法和我们平常用的obj.toString()有一些不同,它是带参数的,一般的toString方法是对象所属类的方法,但是数组不是一个类,所以要将其转成字符串,就要使用Arrays.toString, 这个方法有9个重载类型,包括8个基本类型和一个对象类型,下面列举一个int数组和一个对象数组
public static void IntArrayToString(){
int[] array = {1, 2, 3, 4};
System.out.println(Arrays.toString(array));
}
//Output: [1, 2, 3, 4]
public static void ObjArrayToString(){
Object[] array = new Object[3];
array[0] = new Dish("Fish");
array[1] = new Dish("Meat");
array[2] = new Dish("Vegetable");
System.out.println(Arrays.toString(array));
//Output: [Dish{name='Fish'}, Dish{name='Meat'}, Dish{name='Vegetable'}]
}
private static class Dish{
private final String name;
public Dish(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dish{" +
"name='" + name + '\'' +
'}';
}
}
如果不是调用的toString方法,而是直接将他们输出的话,就会打印他们的类型和内存地址
[I@74a14482 //int数组
[Ljava.lang.Object;@1540e19d //对象数组
排序
Arrays实现了除了布尔类型之外,其他类型(包括对象类型)的排序方法,其中,对象类型的排序需要对象类实现Comparable接口.并且,这个排序方法是原地排序,直接对数组进行操作而不是返回一个新的数组
对int数组进行排序->
public static void IntArraySort(){
int[] array = {5, 2, 7, 4};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
//Output: [2, 4, 5, 7]
}
因为sort方法有对int类型的重载版本,所以直接使用就可以了
对String数组进行排序->
public static void StringArraySort(){
String[] array = {"hello","i","am","boyn"};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
//Output: [am, boyn, hello, i]
}
对于String类型来说,它已经实现了Comparable接口,所以也可以直接调用
对对象数组进行排序->
如果我们的对象没有实现Comparable接口,但是又想对其进行排序,那我们可以传入一个Comparator,比较器对象来进行比较
public static void ObjectArraySort(){
Dish[] array = new Dish[3];
array[1] = new Dish("Meat");
array[2] = new Dish("Vegetable");
array[0] = new Dish("Fish");
Arrays.sort(array, new Comparator<Dish>() {
@Override
public int compare(Dish o1, Dish o2) {
return o1.getName().compareTo(o2.getName());
}
});
//Output: [Dish{name='Fish'}, Dish{name='Meat'}, Dish{name='Vegetable'}]
System.out.println(Arrays.toString(array));
}
当然了,如果熟悉lambda编程的小伙伴,就知道Comparator有一组静态方法叫做comparing,这是一个工厂方法,传入该对象的某个可比较的域,就可以直接返回一个根据这个域来比较的比较器对象,所以我们还可以这样写
public static void ObjectArraySort(){
Dish[] array = new Dish[3];
array[1] = new Dish("Meat");
array[2] = new Dish("Vegetable");
array[0] = new Dish("Fish");
Arrays.sort(array,Comparator.comparing(Dish::getName));
//Output: [Dish{name='Fish'}, Dish{name='Meat'}, Dish{name='Vegetable'}]
System.out.println(Arrays.toString(array));
}
这样看起来,就简单得多了
查找
在Arrays中,一般来说,查找和排序是对应的,所用的是二分查找法,binarySearch(),需要注意的是,这个方法只能够用于已排序的数组,它会返回该元素在数组中的索引,值得一提的是,如果没有找到该元素,那么就会返回一个负数,这个负数等于-(插入点+1),插入点的意思是,如果我们在这个插入点插入这个没有找到的元素,可以保持这个数组有序
那么我们来看一下用法吧
int数组中的查找
public static void IntArraySearch(){
int[] array = {5, 2, 7, 4};
Arrays.sort(array);
System.out.println(Arrays.binarySearch(array,2));
//Output: 0
}
String数组中的查找
public static void StringArraySearch(){
String[] array = {"hello","i","am","boyn"};
Arrays.sort(array);
System.out.println(Arrays.binarySearch(array,"i"));
//Output: 3
}
Object数组中的查找
public static void ObjectArraySearch(){
Dish[] array = new Dish[3];
array[1] = new Dish("Meat");
array[2] = new Dish("Vegetable");
array[0] = new Dish("Fish");
Arrays.sort(array,Comparator.comparing(Dish::getName));
System.out.println(Arrays.binarySearch(array,new Dish("Meat"),Comparator.comparing(Dish::getName)));
//Output: 1
}
部分函数实现原理
查找函数
在上面,我们说到了要调用查找,就要保持数组是有序的,那么为什么呢
因为在Arrays类中,所用的查找是二分查找法,他能够高效(O(log2n))地对有序元素进行查找
有关二分查找的详细知识,可以查看下面这篇wiki
排序函数的设计模式
在我们使用排序函数sort时,他为我们重载了多个版本 ,其中有一个版本的签名是这样的
public static <T> void sort(T[] a, Comparator<? super T> c)
这体现了一种将不变与变化相分离的思维,不变的是排序的步骤,即比较,交换的重复,但是其中,比较这个步骤是可以有不同的,可以根据我们传入不同的比较器,从而有不同的比较方法,这是一种常见的设计模式,成为策略模式,不同的比较方法就是不同的策略
排序函数的实现
对于基本类型的数组,从Java7开始,排序函数就成为了双枢轴快速排序,是一种对快速排序的优化
对于对象类型,Java采用了TimSort,是一种基于插入排序和归并排序的优化
More
在Arrays里面,封装了非常多常用的函数,但是仍然有一些操作是没有覆盖到的,这时候我们可以选用一些比较常用的开源库,如Apache基金会中的commons项目中的 commons.lang3.ArrayUtils,里面有更多的数组操作,如各种检查性的操作,判断非空,有序等等,更多可以参阅其官方文档