数组
定义:
数组是一个简单的复合数据类型,它是一系列有序相同类型数据的集合,可以通过数组名加上一个不会越界下标值来唯一确定数组中的元素。(一个比较特殊的对象,是object的直接子类)。在Java中,数组本身就是一个class。数组有很多种初始化的方式,在初始化的时候时刻要记得数组本身是一个对象,通常需要new一个这样的对象才行。一种特殊的情况是类似于这段代码的声明时隐式初始化: Integer[] a = {new Integer(0),new Integer(0),new Integer(0)}; 不可以如下: Integer[] a; a = {new Integer(0),new Integer(0),new Integer(0)};
数组和容器类三点区别:
效率、类型限定和对于基本类型的处理,效率肯定是内建的数组效率更高一些。在泛型出来之前,容器类都是存取Object,而数组规定了确定类型。在自动封包解包前,容器类不支持基本类型而数组支持。
public class test {
int[] a0 = new int[10];
test t=new test();
System.out.println("一维数组:" + a0.getClass().getName());
int[][] a1 = new int[10][10];
System.out.println("二维数组:" + a1.getClass().getName());
int[][][] a2 = new int[10][10][10];
System.out.println("三维数组:" + a2.getClass());
System.out.println(t.getClass());
}
System.out.println(t.getClass());
}
一维数组:[I;二 维数组:[[I ; 三维数组:class [[[I
[代表维度,普通类是路径加类名作为自己的标识,而数组则已[+数组元素路径+类名作为标识,在数组中有一个特征的属性length,数组直接继承自object,object里没有length变量,那么数组如何实现length的
public static void main(String[] args) {
int a[] = new int[2];
int i = a.length;
}
}
M的class文件,打开字节码如下
0 iconst_2 //将int型常量2压入操作数栈
1 newarray 10 (int) //将2弹出操作数栈作为长度,创建类型为int维度为1的数组,并将数组的引用压入操作数栈
3 astore_1 //将数组的引用从操作数栈中弹出,保存在索引为1的局部变量(即a)中
4 aload_1 //将索引为1的局部变量(即a)压入操作数栈
5 arraylength //从操作数栈弹出数组引用(即a),并获取其长度(JVM负责实现如何获取),并将长度压入操作数栈
6 istore_2 //将数组长度从操作数栈弹出,保存在索引为2的局部变量(即i)中
7 return //m方法返回
这里没有length这个成员变量,arraylength,这条指令是用来获取数组的长度的,JVM对数组的长度做了特殊的处理,它是通过arraylength这条指令来实现的。
数组的应用:
数组要先声明在使用,细节为:声明:数组形式,分配空间:连续存储空间。赋值:数据初始化,基本类型初始值为0,引用类型初始值为null。处理:数据的操作,数组名加有效下标的处理。
数组的性能:数组是一种高效的存储和随机访问对象引用序列的方式。数组在某些地方的使用是不如List和Set集合,数组有自己的优势,集合的底层也是通过数组来实现的。比如在求和的使用中使用数组和list的对比。数组使用get函数获取数据,list使用get()函数。list.get(i)这个动作里进行了拆箱操作,Integer对象对int型数据进行对象封装,这里拆箱和数组获取相比慢了很多。
数组的操作:
数组的长度:数组生明后长度不会改变,但是如果想让其改变,模拟arraylist的扩容方法,将原始数组拷贝至新数组中,新数组的长度时原数组的1.5倍。
数组的复制:使用List.toArray()方法转换成数组然后再通过Arrays.copyOf拷贝,再转换成集合,若数组元素为对象,则数组里面数据是对象引用,典型浅复制。
数组转换为list,Arrays的工具里有asList()方法,将其转化成列表
public static void main(String[] args) {
int[] a = new int[]{1,2,3};
List list = Arrays.asList(a);
System.out.println(list.size());
}
结果是1而不是3,在asList()的源码中T..a参数是泛型变长参数,这里没有报错是因为,数组是对象处理的可以是泛型
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
程序是把一个int型的数组作为了T的类型,所以在转换之后List中就只会存在一个类型为int数组的元素了。所以我们这样的程序System.out.println(datas.equals(list.get(0)));输出结果肯定是true。当然如果将int改为Integer,则长度就会变成3了。
java的Arrays()方法
Arrays提供了丰富的方法,包括sort和binarySearch(数组有序下二分查找)。而sort方法中的排序算法大致是这样一个思路,分类型+分数组长度。Arrays的sort方法针对7个基本类型的数组和Object以及泛型T数组分别给出了多种不同的重载实现。基本类型数组,对于数组长度较短的使用插入排序,而对于长度较大的基本都采用了经过改进的快速排序算法。
对象类型排序,对于短数组一样采用了插入排序,而较长的数组则使用了归并排序。对基本类型和对象类型排序算法的不同(可能是算法的稳定性)快速排序是不稳定的算法,而这对基本类型没什么影响,对于对象类型则不然,故采取了稳定的归并排序算法。较短和较长数组的区分,Arrays里的方法实现设定了一些特定的阈值常量。对于float和double浮点排序,排序算法做了一些特殊处理,进行了排序初始化处理。Java中的八大基本类型,除了boolean,其它七种都是可以且有需求进行排序的,如果一个数组是单一的基础类型,形如int[] data, long data[]都可以直接使用Arrays.sort()进行排序。对于所有可排序的基本类型,都是采用DualPivotQuicksort来进行排序的多路归并思想是:元素个数从1-47,则直接使用插入排序进行排序。元素个数从47-286,则使用多路快速排序。元素个数大于286,则使用归并排序。快排是一个中轴两个指针移动,一次排序划分两部分以中轴为界。
对路快排。1、选取两个中轴P1, P2,假设P1<P2,否则交换。2、过程中原数组会分为四个部分:小于中轴1,大于中轴2,介于两个中轴之间,未排序部分(刚开始除了两个中轴,其它元素都属于这部分)。3、开始后,从未排序部分选取一个数,和两个中轴作比较,然后放到合适的位置,一直到未排序部分无数据,结束一趟排序。4、递归地处理子数组,稳定排序,时间复杂度稳定为O(nlogn)。
引用类型的排序可以用Comparator与Comparable。
参数 | Comparable | Comparator |
---|---|---|
排序逻辑 | 排序逻辑必须在待排序对象的类中,故称之为自然排序 | 排序逻辑在另一个实现 |
实现 | 实现Comparable接口 | 实现Comparator接口 |
排序方法 | int compareTo(Object o1) | int compare(Object o1,Object o2) |
触发排序 | Collections.sort(List) | Collections.sort(List, Comparator) |
接口所在包 | java.lang.Comparable | java.util.Comparator |
Comparator:强行对某个对象collection进行整体排序的比较函数,可以将Comparator传递给Collections.sort或Arrays.sort。接口
/**
* @return o1小于、等于或大于o2,分别返回负整数、零或正整数。
*/
int compare(Object o1, Object o2);
强行对实现它的每个类的对象进行整体排序,实现此接口的对象列表(和数组)可以通过Collections.sort或Arrays.sort进行自动排序。接口:int compareTo(object o)实例:
public class User {
private String id;
private int age;
public User(String id, int age) {
this.id = id;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Comparable的实现
import java.util.Arrays;
public class User implements Comparable {
private String id;
private int age;
public User(String id, int age) {
this.id = id;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int compareTo(Object o) {
return this.age - ((User) o).getAge();
}
public static void main(String[] args) {
User[] users = new User[] { new User("张三", 23), new User("李四", 21) };
Arrays.sort(users);
for (int i = 0; i < users.length; i++) {
User user = users[i];
System.out.println(user.getId() + " " + user.getAge());
}
}
}
Comparator对User集合实现:
import java.util.Arrays;
import java.util.Comparator;
public class UserComparator implements Comparator {
public int compare(Object o1, Object o2) {
return ((User) o1).getAge() - ((User) o2).getAge();
}
public static void main(String[] args) {
User[] users = new User[] { new User("张三", 23), new User("李四", 21) };
Arrays.sort(users, new UserComparator());
for (int i = 0; i < users.length; i++) {
User user = users[i];
System.out.println(user.getId() + " " + user.getAge());
}
}
}
结果:李四:21 张三:23