假设对您有小小的帮助,是我莫大的荣幸!
ps:本文讨论范围为java
一:数组是什么呢?他是一个对象,一个奇妙的对象
public class Test {
public static void main(String[] args) {
int[] array = new int[10];
System.out.println("array的父类是:" + array.getClass().getSuperclass());
System.out.println("array的类名是:" + array.getClass().getName());
}
}
-------Output:
array的父类是:class java.lang.Object
array的类名是:[I
噢,它的类名好奇怪诶:
[I是个神马??它本身都不符合法,所以我觉得一定是SUN那帮天才一定偷偷干了些什么,再看例如以下代码
public class Test {
public static void main(String[] args) {
int[] array_00 = new int[10];
System.out.println("一维数组:" + array_00.getClass().getName());
int[][] array_01 = new int[10][10];
System.out.println("二维数组:" + array_01.getClass().getName());
int[][][] array_02 = new int[10][10][10];
System.out.println("三维数组:" + array_02.getClass().getName());
}
}
-----------------Output:
一维数组:[I
二维数组:[[I
三维数组:[[[I
不能发现:[代表它的维度
临时无论 [I 哪里来的,看以下代码
public class Test {
public static void main(String[] args) {
int[] array = new int[10];
Class clazz = array.getClass();
System.out.println(clazz.getDeclaredFields().length);
System.out.println(clazz.getDeclaredMethods().length);
System.out.println(clazz.getDeclaredConstructors().length);
System.out.println(clazz.getDeclaredAnnotations().length);
System.out.println(clazz.getDeclaredClasses().length);
}
}
----------------Output:
0
0
0
0
0
这,数组类明显是个空类,奇怪的是length方法怎么来的??我们都知道,数组类是Object的子类,而Object也不含length方法,而数组类本身是个空类,由此能够推測:
这种方法一定是JVM偷偷处理的
答案就在以下的代码中:
public class Main {
public static void main(String[] args) {
int a[] = new int[2];
int i = a.length;
}
}
观看编译后的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 //main方法返回
总结下:数组是一个对象,数组名:[I ,数组对象:[Ljava.lang.Object
数组类是一个空对象,它的长度是JVM偷偷处理的
以下我们聊聊第二个问题:
二:为了性能,您还是优先考虑数组吧
在项目设计中数组使用的越来越少了,并且它确实是没有List、Set这些集合使用方便,可是在某些方面数组还是存在一些优势的,比如:速度,并且集合类的底层也都是通过数组来实现的
--------这是ArrayList的add()------ public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }以下利用数组和list来做一些操作比較
Long time1 = System.currentTimeMillis(); for(int i = 0 ; i < 100000000 ;i++){ sum += arrays[i%10]; } Long time2 = System.currentTimeMillis(); System.out.println("数组求和所花费时间:" + (time2 - time1) + "毫秒"); Long time3 = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { sum += list.get(i%10); } Long time4 = System.currentTimeMillis(); System.out.println("List求和所花费时间:" + (time4 - time3) + "毫秒"); --------------Output: 数组求和所花费时间:696毫秒 List求和所花费时间:3498毫秒
从上面的时间消耗上面来说数组对于基本类型的求和计算的速度是集合的5倍左右。事实上在list集合中,求和其中有一个致命的动作:list.get(i)。这个动作是进行拆箱动作,Integer对象通过intValue方法自己主动转换成一个int基本类型,在这里就产生了不必要的性能消耗。
所以:在java中,数组是一种效率最高的存储和随机訪问对象引用序列的方式。
但是实际在软件开发中,非常多同学都喜欢使用List,为什么你会讨厌数组呢?由于他是定长的
变长数组??
数组是定长的,一旦初始化声明后是不可改变长度的。这对我们在实际开发中是很不方便的,聪明的我们肯定是能够找到方法来实现的。就如java不能实现多重继承一样,我们一样能够利用内部类和接口来实现
那么怎样来实现变长数组呢?我们能够利用List集合add方法里面的扩容思路来模拟实现。以下是ArrayList的扩容方法:
public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; /** * 若当前须要的长度超过数组长度时进行扩容处理 */ if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3) / 2 + 1; //扩容 if (newCapacity < minCapacity) newCapacity = minCapacity; //拷贝数组,生成新的数组 elementData = Arrays.copyOf(elementData, newCapacity); } }那么我们能够这样做:让数组自己主动扩容
public class ArrayUtils { /** * @desc 对数组进行扩容 * @author chenssy * @data 2013-12-8 * @param <T> * @param datas 原始数组 * @param newLen 扩容大小 * @return T[] */ public static <T> T[] expandCapacity(T[] datas,int newLen){ newLen = newLen < 0 ? datas.length :datas.length + newLen; //生成一个新的数组 return Arrays.copyOf(datas, newLen); } /** * @desc 对数组进行扩容处理,1.5倍 * @author chenssy * @data 2013-12-8 * @param <T> * @param datas 原始数组 * @return T[] */ public static <T> T[] expandCapacity(T[] datas){ int newLen = (datas.length * 3) / 2; //扩容原始数组的1.5倍 //生成一个新的数组 return Arrays.copyOf(datas, newLen); } /** * @desc 对数组进行扩容处理, * @author chenssy * @data 2013-12-8 * @param <T> * @param datas 原始数组 * @param mulitiple 扩容的倍数 * @return T[] */ public static <T> T[] expandCapacityMul(T[] datas,int mulitiple){ mulitiple = mulitiple < 0 ? 1 : mulitiple; int newLen = datas.length * mulitiple; return Arrays.copyOf(datas,newLen ); } }
通过这样的迂回的方式我们能够实现数组的扩容。因此在项目中假设确实须要变长的数据集,数组也是在考虑范围之内的,我们不能由于他是固定长度而排斥他!
三,数组拷贝的问题
曾经在做集合拷贝的时候因为集合没有拷贝的方法,所以一个一个的复制是很麻烦的,所以我就干脆使用List.toArray()方法转换成数组然后再通过Arrays.copyOf拷贝,在转换成集合,个人认为很方便,殊不知我已经陷入了当中的陷进!我们知道若数组元素为对象,则数组里面数据是对象引用
public class Test { public static void main(String[] args) { Person person_01 = new Person("chenssy_01"); Person[] persons1 = new Person[]{person_01}; Person[] persons2 = Arrays.copyOf(persons1,persons1.length); System.out.println("数组persons1:"); display(persons1); System.out.println("---------------------"); System.out.println("数组persons2:"); display(persons2); //改变其值 persons2[0].setName("chessy_02"); System.out.println("------------改变其值后------------"); System.out.println("数组persons1:"); display(persons1); System.out.println("---------------------"); System.out.println("数组persons2:"); display(persons2); } public static void display(Person[] persons){ for(Person person : persons){ System.out.println(person.toString()); } } } -------------Output: 数组persons1: 姓名是:chenssy_01 --------------------- 数组persons2: 姓名是:chenssy_01 ------------改变其值后------------ 数组persons1: 姓名是:chessy_02 --------------------- 数组persons2: 姓名是:chessy_02
结果:persons1中的值也发生了改变,这是典型的浅拷贝问题。所以通过Arrays.copyOf()方法产生的数组是一个浅拷贝。同一时候数组的clone()方法也是,集合的clone()方法也是,所以我们在使用拷贝方法的同一时候一定要注意浅拷贝这问题。
有关于深浅拷贝的博文,參考:
渐析java的浅拷贝和深拷贝:http://www.cnblogs.com/chenssy/p/3308489.html。
使用序列化实现对象的拷贝:http://blog.csdn.net/chenssy/article/details/12952063
public static void main(String[] args) { int[] datas = new int[]{1,2,3,4,5}; List list = Arrays.asList(datas); System.out.println(list.size()); } ------------Output: 1结果是1,是的你没有看错, 结果就是1。可是为什么会是1而不是5呢?先看asList()的源代码
public static <T> List<T> asList(T... a) { return new ArrayList<T>(a); }
注意这个參数:T…a,这个參数是一个泛型的变长參数,我们知道基本数据类型是不可能泛型化的,也是就说8个基本数据类型是不可作为泛型參数的,可是为什么编译器没有报错呢?这是由于在java中,数组会当做一个对象来处理,它是能够泛型的,所以我们的程序是把一个int型的数组作为了T的类型,所以在转换之后List中就仅仅会存在一个类型为int数组的元素了。所以我们这种程序System.out.println(datas.equals(list.get(0)));输出结果肯定是true。当然假设将int改为Integer,则长度就会变成5了。
我们在看以下程序:
enum Week{Sum,Mon,Tue,Web,Thu,Fri,Sat} public static void main(String[] args) { Week[] weeks = {Week.Sum,Week.Mon,Week.Tue,Week.Web,Week.Thu,Week.Fri}; List<Week> list = Arrays.asList(weeks); list.add(Week.Sat); }这个程序很easy,就是讲一个数组转换成list,然后改变集合中值,可是执行呢?
Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:131) at java.util.AbstractList.add(AbstractList.java:91) at com.array.Test.main(Test.java:18)编译没错,可是执行居然出现了异常错误!UnsupportedOperationException ,当不支持请求的操作时,就会抛出该异常。从某种程度上来说就是不支持add方法,我们知道这是不可能的!什么原因引起这个异常呢?先看asList()的源码:
public static <T> List<T> asList(T... a) { return new ArrayList<T>(a); }这里是直接返回一个ArrayList对象返回,可是注意这个ArrayList并非java.util.ArrayList,而是Arrays工具类的一个内之类:
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
/** 省略方法 **/
}
可是这个内部类并没有提供add()方法,那么查看父类public boolean add(E e) { add(size(), e); return true; } public void add(int index, E element) { throw new UnsupportedOperationException(); }
这里父类只不过提供了方法,方法的详细实现却没有,所以详细的实现须要子类自己来提供,可是很遗憾
这个内部类ArrayList并没有提高add的实现方法。在ArrayList中,它主要提供了例如以下几个方法:
1、size:元素数量
2、toArray:转换为数组,实现了数组的浅拷贝。
3、get:获得指定元素。
4、contains:是否包括某元素。
所以综上所述,asList返回的是一个长度不可变的列表。数组是多长,转换成的列表是多长,我们是无法通过add、remove来添加或者降低其长度的。
參考文献:《编写高质量代码--改善Java程序的151个建议》
原文參考:http://blog.csdn.net/chenssy/article/details/17732783