java数组解析_Java - 数组解析
一、什么是數(shù)組
數(shù)組?什么是數(shù)組?在我印象中的數(shù)組是應該這樣的:通過new關鍵字創(chuàng)建并組裝他們,通過使用整形索引值訪問它的元素,并且它的尺寸是不可變的!
但是這只是數(shù)組的最表面的東西!深一點?就是這樣:數(shù)組是一個簡單的復合數(shù)據(jù)類型,它是一系列有序數(shù)據(jù)的集合,它當中的每一個數(shù)據(jù)都具有相同的數(shù)據(jù)類型,我們通過數(shù)組名加上一個不會越界下標值來唯一確定數(shù)組中的元素。
不管在其他語言中數(shù)組是什么,在java中它就是對象。一個比較特殊的對象。
public classTest {public static voidmain(String[] args) {int[] array = new int[10];
System.out.println("array的父類是:" +array.getClass().getSuperclass());
System.out.println("array的類名是:" +array.getClass().getName());
}
}-------Output:
array的父類是:classjava.lang.Object
array的類名是:[I
從上面示例可以看出,數(shù)組的是Object的直接子類,它屬于“第一類對象”,但是它又與普通的java對象存在很大的不同,從它的類名就可以看出:[I,這是什么東東??在JDK中我就沒有找到這個類,話說這個"[I”都不是一個合法標識符。怎么定義成類啊?所以我認為SUM那幫天才肯定對數(shù)組的底層肯定做了特殊的處理。
我們再看如下示例:
public classTest {public static voidmain(String[] args) {int[] array_00 = new int[10];
System.out.println("一維數(shù)組:" +array_00.getClass().getName());int[][] array_01 = new int[10][10];
System.out.println("二維數(shù)組:" +array_01.getClass().getName());int[][][] array_02 = new int[10][10][10];
System.out.println("三維數(shù)組:" +array_02.getClass().getName());
}
}-----------------Output:
一維數(shù)組:[I
二維數(shù)組:[[I
三維數(shù)組:[[[I
通過這個實例我們知道:[代表了數(shù)組的維度,一個[表示一維,兩個[表示二維。可以簡單的說數(shù)組的類名由若干個'['和數(shù)組元素類型的內(nèi)部名稱組成。不清楚我們再看:
public classTest {public static voidmain(String[] args) {
System.out.println("Object[]:" + Object[].class);
System.out.println("Object[][]:" + Object[][].class);
System.err.println("Object[][][]:" + Object[][][].class);
System.out.println("Object:" + Object.class);
}
}---------Output:
Object[]:class[Ljava.lang.Object;
Object[][]:class[[Ljava.lang.Object;
Object[][][]:class[[[Ljava.lang.Object;
Object:class java.lang.Object
從這個實例我們可以看出數(shù)組的“廬山真面目”。同時也可以看出數(shù)組和普通的Java類是不同的,普通的java類是以全限定路徑名+類名來作為自己的唯一標示的,而數(shù)組則是以若干個[+L+數(shù)組元素類全限定路徑+類來最為唯一標示的。這個不同也許在某種程度上說明了數(shù)組也普通java類在實現(xiàn)上存在很大的區(qū)別,也許可以利用這個區(qū)別來使得JVM在處理數(shù)組和普通java類時作出區(qū)分。
我們暫且不論這個[I是什么東東,是由誰來聲明的,怎么聲明的(這些我現(xiàn)在也不知道!但是有一點可以確認:這個是在運行時確定的)。先看如下:
public classTest {public static voidmain(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
從這個運行結果可以看出,我們親愛的[I沒有生命任何成員變量、成員方法、構造函數(shù)、Annotation甚至連length成員變量這個都沒有,它就是一個徹徹底底的空類。沒有聲明length,那么我們array.length時,編譯器怎么不會報錯呢?確實,數(shù)組的length是一個非常特殊的成員變量。我們知道數(shù)組的是Object的直接之類,但是Object是沒有l(wèi)ength這個成員變量的,那么length應該是數(shù)組的成員變量,但是從上面的示例中,我們發(fā)現(xiàn)數(shù)組根本就沒有任何成員變量,這兩者不是相互矛盾么?
打開class文件,得到main方法的字節(jié)碼:
0 iconst_2 //將int型常量2壓入操作數(shù)棧
1 newarray 10 (int) //將2彈出操作數(shù)棧,作為長度,創(chuàng)建一個元素類型為int, 維度為1的數(shù)組,并將數(shù)組的引用壓入操作數(shù)棧
3 astore_1 //將數(shù)組的引用從操作數(shù)棧中彈出,保存在索引為1的局部變量(即a)中
4 aload_1 //將索引為1的局部變量(即a)壓入操作數(shù)棧
5 arraylength //從操作數(shù)棧彈出數(shù)組引用(即a),并獲取其長度(JVM負責實現(xiàn)如何獲取),并將長度壓入操作數(shù)棧
6 istore_2 //將數(shù)組長度從操作數(shù)棧彈出,保存在索引為2的局部變量(即i)中
7 return //main方法返回
在這個字節(jié)碼中我們還是沒有看到length這個成員變量,但是看到了這個:arraylength ,這條指令是用來獲取數(shù)組的長度的,所以說JVM對數(shù)組的長度做了特殊的處理,它是通過arraylength這條指令來實現(xiàn)的。
二、數(shù)組的使用方法
通過上面算是對數(shù)組是什么有了一個初步的認識,下面將簡單介紹數(shù)組的使用方法。
數(shù)組的使用方法無非就是四個步驟:聲明數(shù)組、分配空間、賦值、處理。
聲明數(shù)組:就是告訴計算機數(shù)組的類型是什么。有兩種形式:int[] array、int array[]。
分配空間:告訴計算機需要給該數(shù)組分配多少連續(xù)的空間,記住是連續(xù)的。array = new int[10];
賦值:賦值就是在已經(jīng)分配的空間里面放入數(shù)據(jù)。array[0] = 1 、array[1] = 2……其實分配空間和賦值是一起進行的,也就是完成數(shù)組的初始化。有如下三種形式:
int a[] = new int[2]; //默認為0,如果是引用數(shù)據(jù)類型就為null
int b[] = new int[] {1,2,3,4,5};int c[] = {1,2,3,4,5};
三、性能?請優(yōu)先考慮數(shù)組
在java中有很多方式來存儲一系列數(shù)據(jù),而且在操作上面比數(shù)組方便的多?但為什么我們還需要使用數(shù)組,而不是替代它呢?數(shù)組與其他種類的容器之間的區(qū)別有三個方面:效率、類型和保存基本類型的能力。在java中,數(shù)組是一種效率最高的存儲和隨機訪問對象引用序列的方式。
在項目設計中數(shù)組使用的越來越少了,而且它確實是沒有List、Set這些集合使用方便,但是在某些方面數(shù)組還是存在一些優(yōu)勢的,例如:速度,而且集合類的底層也都是通過數(shù)組來實現(xiàn)的。
--------這是ArrayList的add()------
public booleanadd(E e) {
ensureCapacity(size+ 1); //Increments modCount!!
elementData[size++] =e;return true;
}
下面利用數(shù)組和list來做一些操作比較。
一、求和
Long time1 =System.currentTimeMillis();for(int i = 0 ; i < 100000000 ;i++){
sum+= arrays[i%10];
}
Long time2=System.currentTimeMillis();
System.out.println("數(shù)組求和所花費時間:" + (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:
數(shù)組求和所花費時間:696毫秒
List求和所花費時間:3498毫秒
從上面的時間消耗上面來說數(shù)組對于基本類型的求和計算的速度是集合的5倍左右。其實在list集合中,求和當中有一個致命的動作:list.get(i)。這個動作是進行拆箱動作,Integer對象通過intValue方法自動轉換成一個int基本類型,在這里就產(chǎn)生了不必要的性能消耗。
所以在性能要求較高的場景中請優(yōu)先考慮數(shù)組。
四、變長數(shù)組?
數(shù)組是定長的,一旦初始化聲明后是不可改變長度的。這對我們在實際開發(fā)中是非常不方便的,聰明的我們肯定是可以找到方法來實現(xiàn)的。就如java不能實現(xiàn)多重繼承一樣,我們一樣可以利用內(nèi)部類和接口來實現(xiàn)(請參考:java提高篇(九)-----實現(xiàn)多重繼承)。
那么如何來實現(xiàn)變長數(shù)組呢?我們可以利用List集合add方法里面的擴容思路來模擬實現(xiàn)。下面是ArrayList的擴容方法:
public void ensureCapacity(intminCapacity) {
modCount++;int oldCapacity =elementData.length;/*** 若當前需要的長度超過數(shù)組長度時進行擴容處理*/
if (minCapacity >oldCapacity) {
Object oldData[]=elementData;int newCapacity = (oldCapacity * 3) / 2 + 1; //擴容
if (newCapacity
newCapacity=minCapacity;//拷貝數(shù)組,生成新的數(shù)組
elementData =Arrays.copyOf(elementData, newCapacity);
}
}
這段代碼對我們有用的地方就在于if語句后面。它的思路是將原始數(shù)組拷貝到新數(shù)組中,新數(shù)組是原始數(shù)組長度的1.5倍。所以模擬的數(shù)組擴容代碼如下:
public classArrayUtils {/*** @desc 對數(shù)組進行擴容
*@authorchenssy
* @data 2013-12-8
*@param
*@paramdatas 原始數(shù)組
*@paramnewLen 擴容大小
*@returnT[]*/
public static T[] expandCapacity(T[] datas,intnewLen){
newLen= newLen < 0 ? datas.length :datas.length +newLen;//生成一個新的數(shù)組
returnArrays.copyOf(datas, newLen);
}/*** @desc 對數(shù)組進行擴容處理,1.5倍
*@authorchenssy
* @data 2013-12-8
*@param
*@paramdatas 原始數(shù)組
*@returnT[]*/
public static T[] expandCapacity(T[] datas){int newLen = (datas.length * 3) / 2; //擴容原始數(shù)組的1.5倍//生成一個新的數(shù)組
returnArrays.copyOf(datas, newLen);
}/*** @desc 對數(shù)組進行擴容處理,
*@authorchenssy
* @data 2013-12-8
*@param
*@paramdatas 原始數(shù)組
*@parammulitiple 擴容的倍數(shù)
*@returnT[]*/
public static T[] expandCapacityMul(T[] datas,intmulitiple){
mulitiple= mulitiple < 0 ? 1: mulitiple;int newLen = datas.length *mulitiple;returnArrays.copyOf(datas,newLen );
}
}
通過這種迂回的方式我們可以實現(xiàn)數(shù)組的擴容。因此在項目中如果確實需要變長的數(shù)據(jù)集,數(shù)組也是在考慮范圍之內(nèi)的,我們不能因為他是固定長度而排斥他!
五、數(shù)組復制問題
以前在做集合拷貝的時候由于集合沒有拷貝的方法,所以一個一個的復制是非常麻煩的,所以我就干脆使用List.toArray()方法轉換成數(shù)組然后再通過Arrays.copyOf拷貝,在轉換成集合,個人覺得非常方便,殊不知我已經(jīng)陷入了其中的陷進!我們知道若數(shù)組元素為對象,則數(shù)組里面數(shù)據(jù)是對象引用
public classTest {public static voidmain(String[] args) {
Person person_01= new Person("chenssy_01");
Person[] persons1= newPerson[]{person_01};
Person[] persons2=Arrays.copyOf(persons1,persons1.length);
System.out.println("數(shù)組persons1:");
display(persons1);
System.out.println("---------------------");
System.out.println("數(shù)組persons2:");
display(persons2);//改變其值
persons2[0].setName("chessy_02");
System.out.println("------------改變其值后------------");
System.out.println("數(shù)組persons1:");
display(persons1);
System.out.println("---------------------");
System.out.println("數(shù)組persons2:");
display(persons2);
}public static voiddisplay(Person[] persons){for(Person person : persons){
System.out.println(person.toString());
}
}
}-------------Output:
數(shù)組persons1:
姓名是:chenssy_01---------------------數(shù)組persons2:
姓名是:chenssy_01------------改變其值后------------數(shù)組persons1:
姓名是:chessy_02---------------------數(shù)組persons2:
姓名是:chessy_02
從結果中發(fā)現(xiàn),persons1中的值也發(fā)生了改變,這是典型的淺拷貝問題。所以通過Arrays.copyOf()方法產(chǎn)生的數(shù)組是一個淺拷貝。同時數(shù)組的clone()方法也是,集合的clone()方法也是,所以我們在使用拷貝方法的同時一定要注意淺拷貝這問題。
六、數(shù)組轉換為List注意地方
我們經(jīng)常需要使用到Arrays這個工具的asList()方法將其轉換成列表。方便是方便,但是有時候會出現(xiàn)莫名其妙的問題。如下:
public static voidmain(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 ListasList(T... a) {return new ArrayList(a);
}
注意這個參數(shù):T…a,這個參數(shù)是一個泛型的變長參數(shù),我們知道基本數(shù)據(jù)類型是不可能泛型化的,也是就說8個基本數(shù)據(jù)類型是不可作為泛型參數(shù)的,但是為什么編譯器沒有報錯呢?這是因為在java中,數(shù)組會當做一個對象來處理,它是可以泛型的,所以我們的程序是把一個int型的數(shù)組作為了T的類型,所以在轉換之后List中就只會存在一個類型為int數(shù)組的元素了。所以我們這樣的程序System.out.println(datas.equals(list.get(0)));輸出結果肯定是true。當然如果將int改為Integer,則長度就會變成5了。
我們在看下面程序:
enumWeek{Sum,Mon,Tue,Web,Thu,Fri,Sat}public static voidmain(String[] args) {
Week[] weeks={Week.Sum,Week.Mon,Week.Tue,Week.Web,Week.Thu,Week.Fri};
List list =Arrays.asList(weeks);
list.add(Week.Sat);
}
這個程序非常簡單,就是講一個數(shù)組轉換成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)
編譯沒錯,但是運行竟然出現(xiàn)了異常錯誤!UnsupportedOperationException ,當不支持請求的操作時,就會拋出該異常。從某種程度上來說就是不支持add方法,我們知道這是不可能的!什么原因引起這個異常呢?先看asList()的源代碼:
public static ListasList(T... a) {return new ArrayList(a);
}
這里是直接返回一個ArrayList對象返回,但是注意這個ArrayList并不是java.util.ArrayList,而是Arrays工具類的一個內(nèi)之類:
private static class ArrayList extends AbstractList
implementsRandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private finalE[] a;
ArrayList(E[] array) {if (array==null)throw newNullPointerException();
a=array;
}/**省略方法 **/}
但是這個內(nèi)部類并沒有提供add()方法,那么查看父類:
public booleanadd(E e) {
add(size(), e);return true;
}public void add(intindex, E element) {throw newUnsupportedOperationException();
}
這里父類僅僅只是提供了方法,方法的具體實現(xiàn)卻沒有,所以具體的實現(xiàn)需要子類自己來提供,但是非常遺憾
這個內(nèi)部類ArrayList并沒有提高add的實現(xiàn)方法。在ArrayList中,它主要提供了如下幾個方法:
1、size:元素數(shù)量
2、toArray:轉換為數(shù)組,實現(xiàn)了數(shù)組的淺拷貝。
3、get:獲得指定元素。
4、contains:是否包含某元素。
所以綜上所述,asList返回的是一個長度不可變的列表。數(shù)組是多長,轉換成的列表是多長,我們是無法通過add、remove來增加或者減少其長度的。
總結
以上是生活随笔為你收集整理的java数组解析_Java - 数组解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中forward的作用_基于
- 下一篇: 日语输入法 android8.0,讯飞输