雖然學習java也有幾個月了,但是發現自己對java數組的使用還是顯得不夠熟練,比如說一個比較簡單的自己指定對象數組的排序規則(類似于在C++中的重載sort()的comp()函數)就不知道怎麼寫。另外java數組的一些常用函數也不是很熟悉。是以借着學習這一章的機會,好好的做個總結。不過值得注意的是在集合架構中支援泛型後,我們應該盡量使用ArrayList而不是數組了。下面以小樣例的形式展示常見的數組用法。
1).數組的多種初始化方式
下面總結了初始化數組的多種方式,以及如何對指向數組的引用指派,使其指向另一個數組對象。值得注意的是:對象數組和普通數組的各種操作基本上都是一樣的;要說有什麼不同的話就是對象數組預設值為null,而基本數組視本身情況而定。
package lkl;
import java.util.Arrays;
///下面示範了數組的初始化
//包括對象數組和普通數組
class Base{
private static long counter;
private final long id = counter++;
public String toString(){
return "Base"+id;
}
}
public class ArrayOptions {
public static void main(String[] args){
Base[] a; ///隻聲明數組而不配置設定空間
Base[] b = new Base[5]; ///配置設定空間,預設初始化為null
System.out.println("b: "+Arrays.toString(b));
Base[] c=new Base[4];
for(int i=0;i<c.length;i++){
if(c[i]==null){
c[i]=new Base();
}
}
Base[] d={new Base(),new Base(),new Base()}; ///聚集初始化文法(隐式的使用new)
a=new Base[]{new Base(),new Base(),new Base()}; //動态聚集初始化
System.out.println("a.length= "+a.length);
System.out.println("b.length= "+b.length);
System.out.println("c.length= "+c.length);
System.out.println("d.lenght= "+d.length);
a=d;
System.out.println("a.lenght= "+a.length);
int[] e;
int[] f=new int[7]; ///配置設定空間以後會預設初始化為0
System.out.println(Arrays.toString(f));
int[] g=new int[4];
for(int i=0;i<g.length;i++){
g[i]=i*i;
}
int[] h={1,2,3,4};
//沒有初始化就引用length,編譯錯誤
//System.out.println("e.lenght= "+e.length);
System.out.println("f.length= "+f.length);
System.out.println("g.length= "+g.length);
System.out.println("h.length= "+h.length);
e=h;
System.out.println("e.length= "+e.length);
e=new int[]{3,4,3};
System.out.println("e.length= "+e.length);
}/*Output
b: [null, null, null, null, null]
a.length= 3
b.length= 5
c.length= 4
d.lenght= 3
a.lenght= 3
[0, 0, 0, 0, 0, 0, 0]
f.length= 7
g.length= 4
h.length= 4
e.length= 4
e.length= 3
*/
}
2). 從函數中傳回一個數組
對于這種情況,如果在C/C++中我們可能會選擇傳回一個指針,因為不可能直接傳回一個數組。但是在java裡允許我們直接傳回一個數組(當然實際上來講還是一個引用);并且使用完了後我們也不需要手動的銷毀數組,垃圾回收器會幫助我們進行清理。是以傳回一個數組和傳回一個普通值就沒什麼兩樣了。
package lkl;
import java.util.*;
///傳回一個數組
///函數可以顯示的聲明傳回一個數組
///這與傳回其它類型并沒有什麼不同,實際上都是傳回一個引用
//我們也不需要去手動的釋放這個數組的記憶體,在适當的時候垃圾回收器會情理掉它的
public class ReturnArray {
private Random rand = new Random();
public int[] fun(int n){ ///傳回一個具有大小為n的int類型數組
int[] a=new int[n];
for(int i=0;i<n;i++)
a[i]=rand.nextInt(1000);
return a;
}
public static void main(String[] args){
ReturnArray ret = new ReturnArray();
for(int i=1;i<=10;i++){
System.out.println(Arrays.toString(ret.fun(i)));
}
}/*
[627]
[181, 296]
[337, 415, 233]
[617, 607, 956, 897]
[985, 847, 965, 620, 302]
[955, 727, 320, 328, 741, 631]
[298, 532, 478, 116, 448, 718, 754]
[722, 531, 491, 183, 560, 410, 657, 20]
[95, 9, 491, 277, 431, 401, 556, 101, 871]
[600, 311, 221, 761, 9, 689, 664, 40, 167, 636]
*/
}
3).多元數組
java中的多元數組和C++中是類似的。
package lkl;
import java.util.Arrays;
import java.util.*;
///多元數組的建立和初始化
public class Multi {
public static void main(String[] args){
int[][] a={{1,2,3},{4,5,6}};///花括号可以分隔每維
int[][] b=new int[2][4]; ///預設初始化為0
Base[][] c = new Base[2][4]; ///對于對象數組也沒有什麼不同
///deepToString()方法可以将多元數組轉換成多個String
System.out.println(Arrays.deepToString(a));
System.out.println(Arrays.deepToString(b));
System.out.println(Arrays.deepToString(c));
///數組中構成矩陣的每個向量都可以具有任意的長度
Random rand = new Random(56);
int[][][] d=new int[rand.nextInt(8)][][];
for(int i=0;i<d.length;i++){
d[i]=new int[rand.nextInt(5)][];
for(int j=0;j<d[i].length;j++){
d[i][j]=new int[rand.nextInt(4)];
}
}
System.out.println(Arrays.deepToString(d));
}
/*Output
[[1, 2, 3], [4, 5, 6]]
[[0, 0, 0, 0], [0, 0, 0, 0]]
[[null, null, null, null], [null, null, null, null]]
[[[0], [0, 0, 0], [0]], [[0], []], [[], [0]], [], [[0, 0], [0, 0, 0], [0, 0], [0, 0]]]
*/
}
4).數組的填充函數Arrays.fill()
我們可以調用這個函數使用指定的值對數組進行填充,但是這種填充和C++中的memset()不一樣,它不是整塊的記憶體複制,實際上仍是使用for循環進行指派。這就導緻我們可以指定任意的填充值,但是相應的其效率就不是那麼令人滿意了。這個函數适用與各種數組,并且還有一個重載版本可以讓我們指定填充的起點和終點。
package lkl;
import java.util.Arrays;
public class Functiion {
public static void main(String[] args){
//fill()函數可以允許我們用一個固定的值來進行填充
///它的另一種重載形式可以運作我們指定填充的起點和終點
int[] a=new int[10];
Arrays.fill(a, 1);
System.out.println(Arrays.toString(a));
Arrays.fill(a, 0, 4, 2); ///這種格式調用時注意起點包括但終點不包括
Arrays.fill(a, 4,10,4);
System.out.println(Arrays.toString(a));
}/*Output
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[2, 2, 2, 2, 4, 4, 4, 4, 4, 4]
*/
}
5).數組的整體複制和比較
數組的整體複制使用的不是Arrays包下的函數而是System包下的arraycopy()函數。arraycopy()函數所需要的參數有:源數組,表示從源數組複制的起始位置,目的數組,表示目的數組開始指派的起始位置,需要複制元素的個數。因為這個函數底層使用的整塊記憶體的一個複制,是以效率上要比for循環好很多。
另外,對于對象數組我們複制的隻是對象的引用,而不是對象本身的拷貝。并且這個函數也不會自動的執行自動包裝和拆包,是以兩個數組必須要有相同的類型。
package lkl;
import java.util.Arrays;
//System類提供了一個arraycopy()函數用來進行數組的複制
//這種複制比for循環快的多
//arraycopy須要的參數有:源數組,表示從源數組開始複制的位置,
///目的數組,複制到目的數組的位置,以及複制元素的個數
///另外arraycopy()不會自動包裝和自動拆包,在複制對象數組時複制的隻有
//引用,而不是對對象進行拷貝
public class ArrayCopy {
public static void main(String[] args){
int[] a=new int[4];
int[] b=new int[6];
Arrays.fill(a, 10);
Arrays.fill(b, 20);
System.out.println("b= "+Arrays.toString(b));
System.arraycopy(a, 0, b, 2, 3);
System.out.println("b= "+Arrays.toString(b));
Integer[] c=new Integer[10];
Integer[] d=new Integer[6];
Arrays.fill(c, new Integer(3));
Arrays.fill(d, new Integer(5));
System.out.println("c= "+Arrays.toString(c));
System.arraycopy(d, 1, c, 2, 3);
System.out.println("c= "+Arrays.toString(c));
}/*Output
b= [20, 20, 20, 20, 20, 20]
b= [20, 20, 10, 10, 10, 20]
c= [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
c= [3, 3, 5, 5, 5, 3, 3, 3, 3, 3]
*/
}
Arrays包下提供了equals()方法用于整個數組的比較。兩個數組相等的标準是:具有相同的尺寸,并且對應位上的元素相等(通過對每一個元素使用equals()作比較來進行判斷)。如果數組中裝的是我們自己定義的對象引用,那麼要確定重寫了equals()方法,否則比較就會出錯。
package lkl;
import java.util.Arrays;
//用于判斷數組是否相等的方法
///Arrays.equals()
//兩個數組相等的定義為數組的尺寸相同并且對應位元素相同
class MyClass{
private int i;
public MyClass(int i){
this.i=i;
}
@Override//重寫equals()函數
public boolean equals(Object t){
if(t==null) return false;
if(t.getClass()!=MyClass.class)
return false;
return ((MyClass)t).i==this.i;
}
}
public class ArrayEquals {
public static void main(String[] args){
int[] a1=new int[10];
int[] a2=new int[10];
Arrays.fill(a1, 11);
Arrays.fill(a2, 11);
System.out.println(Arrays.equals(a1, a2));
a1[2]=10;
System.out.println(Arrays.equals(a1, a2));
//如果是我們自己定義類型的數組就有可能出問題了
///必須要自己重寫equals()函數,提供相等的标準
MyClass[] m1=new MyClass[4];
MyClass[] m2=new MyClass[4];
Arrays.fill(m2, new MyClass(3));
Arrays.fill(m1, new MyClass(3));
System.out.println("m1==m2? "+Arrays.equals(m1,m2));
}/*
true
false
m1==m2? true
*/
}
6).自定義元素的比較規則
有時候我們需要自己定義數組元素的比較規則,以便進行排序和查找等操作。java提供了兩種方法來提供比較的功能。一種是實作java.lang.Comparable接口,使你的類具有天生的比較能力,此接口很簡單,隻有compareTo()一個方法。此方法接收另一個Object為參數,如果目前對象小于參數則傳回負值,相等傳回0,大于傳回正值。下面的類實作了Comparable接口,我們用排序函數進行檢驗。
package lkl;
import java.lang.*;
import java.util.*;
//有時候我們需要自定義如何比較對象
//為此有兩種方法可以實作,第一種就是實作java.lang.Comparable接口
//另一種方法就是實習Comparator接口
public class CompType implements Comparable<CompType>{
int i;
int j;
public CompType(int n1,int n2){
i=n1; j=n2;
}
public String toString(){
return "[ i= "+i+" j= "+j+"]";
}
//先按i的大小排序,i相等就按j大小排序
///實作Comparable接口就必須實作CompareTo()方法
///compareTo()的傳回值隻有三種,-1,0,1,分别表示小于,等于,大于
public int compareTo(CompType T){
if(i==T.i){
return (j<T.j?-1:(j==T.j?0:1));
}
return (i<T.i?-1:(i==T.i?0:1));
}
public static void main(String[] args){
Random rand = new Random(45);
CompType[] ct=new CompType[4];
for(int i=0;i<4;i++)
ct[i]=new CompType(rand.nextInt(10),rand.nextInt(12));
System.out.println("排序之前: ");
for(int i=0;i<4;i++){
System.out.println(ct[i]);
}
Arrays.sort(ct);
System.out.println("\n排序之後:");
for(int i=0;i<4;i++){
System.out.println(ct[i]);
}
}/*
排序之前:
[ i= 9 j= 3]
[ i= 1 j= 8]
[ i= 7 j= 3]
[ i= 1 j= 3]
排序之後:
[ i= 1 j= 3]
[ i= 1 j= 8]
[ i= 7 j= 3]
[ i= 9 j= 3]
*/
}
第二種方法就就是建立一個實作了Comparator接口的類,将其一個對象作為比較标準傳入排序函數中。這個類有compare(),equals()兩個方法。因為每個類都會繼承Object類的equals()方法,是以一般情況下我們隻需要實作compare()作為比較标準就行了。
package lkl;
import java.util.*;
///自己實作Comparator接口,定義排序标準,實作compare()函數
///排序時傳入一個Comparator對象
///下面的定義表示的是處理CompType類型的比較
class MyComparator implements Comparator<CompType>{
public int compare(CompType t1,CompType t2){
if(t1.i==t2.i){
return (t1.j<t2.j?-1:(t1.j==t2.j?0:1));
}
return (t1.i<t2.i?-1:(t1.j==t2.j?0:1));
}
}
public class ComapratorTest {
public static void main(String[] args){
Random rand = new Random(45);
CompType[] ct=new CompType[4];
for(int i=0;i<4;i++)
ct[i]=new CompType(rand.nextInt(10),rand.nextInt(12));
System.out.println("排序之前: ");
for(int i=0;i<4;i++){
System.out.println(ct[i]);
}
Arrays.sort(ct,new MyComparator());
System.out.println("\n排序之後:");
for(int i=0;i<4;i++){
System.out.println(ct[i]);
}
}
}
7).數組的排序和查找
使用内置的排序方法,可以對任意的基本類型數組進行排序,也可以對任意的對象數組進行排序。隻要該對象實作了Comparable接口或則是具有相關聯的Comparator。對于查找,隻要數組已經排好序,我們就可以使用Arrays.binarySearch()執行快速的二分查找。如果查找到了該元素,傳回其位置。如果傳回的是一個負值,說明數組中不包含此元素,這個負值還表示該元素應該插入的位置。值得注意的是,如果使用Comparator排序了某個對象數組(基本數組是不能用Comparator進行排序的),那麼在對其進行二分時也要傳入一個相同的Comparator對象(使用一個重載函數版本)。
package lkl;
import java.util.*;
///數組的排序與查找問題
///java中内置了排序算法,可以對各種類型的數組進行排序
///隻要該對象實作了Comparable接口或具有相關聯的Comparator
///java标準類庫中的排序算法對正排序的特殊類型進行了優化---針對基本類型設計的
//“快速排序”以及針對對象設計的穩定歸并排序
///對排序以後的數組我們可以直接使用内置的二分查找
//如果使用了Comparator排序了某個對象數組(基本類型數組無法使用Comparator進行排序)
//在使用binarySearch()時也需要提供同樣的
public class ArraySort {
public static void main(String[] args){
Random rand= new Random(330);
int[] a=new int[10];
for(int i=0;i<10;i++)
a[i]=rand.nextInt(20);
int t=a[4];
System.out.println("排序前: ");
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println("排序後:");
System.out.println(Arrays.toString(a));
System.out.println("二分查找"+t+"的位置為為:"+Arrays.binarySearch(a, t));
///下面是對前面對象數組二分的示範
CompType[] ct=new CompType[4];
for(int i=0;i<4;i++)
ct[i]=new CompType(rand.nextInt(10),rand.nextInt(12));
CompType T =ct[2];
System.out.println("T= "+T);
Arrays.sort(ct,new MyComparator());
System.out.println("排序之後");
for(int i=0;i<4;i++){
System.out.println(ct[i]);
}
System.out.println("T的位置為"+Arrays.binarySearch(ct, T,new MyComparator()));
}
}