2014年4月2日 星期三

Java 複習筆記 : 陣列 (續)

類別庫 java.util 有許多好用工具, 其中 java.util.Arrays 是處理陣列的好幫手. 以下繼續整理常用的陣列方法. 參考 :

http://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html 

1. 清除陣列中重複的元素 :

在我的常用 Javascript 函式庫中有一個改寫自 JSLIB 網站的陣列函式 unique_array() , 可以將陣列中的重複元素清除掉. 參考 :

# http://www.jslab.dk/library/Array
# Get unique values from a list in python

範例如下 :

import java.util.*;
import java.text.*;
public class mytest {
  public static void main(String[] args) {
    String[] arr={"楊過","小龍女","公孫止","楊過"};
    arr=unique(arr);
    System.out.println(Arrays.toString(arr));  //輸出 [楊過, 小龍女, 公孫止]
    System.out.println(Arrays.asList(arr));     //輸出 [楊過, 小龍女, 公孫止]
    System.out.println(Arrays.deepToString(arr));  //輸出 [楊過, 小龍女, 公孫止]
    for (String el:arr) {System.out.print(el + " ");}  //輸出 楊過, 小龍女, 公孫止
    }
  public static String[] unique(String[] arr) {
    Set temp=new LinkedHashSet(Arrays.asList(arr));
    return temp.toArray(new String[temp.size()]);
    }
  }

在此例的 unique() 方法中, 利用 Arrays.asList() 把陣列轉成 List, 再轉成 LiskedHashSet 物件, 從而消除了重複的元素, 因為集合之元素不允許重複, 然後再轉回陣列.

同時, 我們也展示了顯示陣列內容的四種方式 : 即呼叫 toString(), asList(), deepToString() 三種方法, 以及傳統的 for each 迴圈. 其中 toString() 可用於任何資料型態陣列, 而 deepToString() 則只能用於物件陣列.

如果將 toString() 用在物件陣列, 將輸出各物件的參考記憶體代號, 而不是各物件的內容. 例如下列範例所示 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    String[] arr1={"楊過","小龍女","公孫止","楊過"};
    int[] arr2={97,85,78,100,98};
    Object[] arr={arr1,arr2};
    System.out.println(Arrays.toString(arr));
    System.out.println(Arrays.deepToString(arr));
    }
  }

輸出結果為 :

[[Ljava.lang.String;@290fbc, [I@1c80b01]
[[楊過, 小龍女, 公孫止, 楊過], [97, 85, 78, 100, 98]]

可見物件陣列必須用 deepToString() 才會顯示陣列內各物件之內容.

2. 複製陣列 :

類別 java.util.Arrays 有一個 copyOf() 方法可以複製陣列實體, 此方法對 8 種原始資料型別都實作了方法多載, 其 API 如下 :

T copyOf(T arr, int newLength)

其第二個參數為新陣列的長度, 若比原陣列長, 則多出來的元素在整數與實數會填 0, 布林會填 false, 字元填空字元, 字串或物件則填 null. 若比原陣列短, 則多出來的部分會被截掉, 如下列範例所示 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[] i={0,1,2,3,4,5,6,7,8,9};
    char[] c={'0','1','2','3','4','5','6','7','8','9'};
    String[] s={"0","1","2","3","4","5","6","7","8","9"};
    boolean[] b={true,true,true,true,true,true,true,true,true,true};
    int[] i1=Arrays.copyOf(i,11);
    int[] i2=Arrays.copyOf(i,9);
    char[] c1=Arrays.copyOf(c,11);
    String[] s1=Arrays.copyOf(s,11);
    boolean[] b1=Arrays.copyOf(b,11);
    System.out.println(Arrays.toString(i1));
    System.out.println(Arrays.toString(i2));
    System.out.println(Arrays.toString(c1));
    System.out.println(Arrays.toString(s1));
    System.out.println(Arrays.toString(b1));
    }
  }

執行結果 :

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,  ]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, null]
[true, true, true, true, true, true, true, true, true, true, false]

可見陣列複製到較長之新陣列會自動補足 (padding), 較短則直接截掉 (truncating). 截掉的情形其實就是取得子陣列, 如同 Javascript 的 slice() 方法.

另外一個複製陣列的方法是 copyOfRange, 用來複製陣列中一段連續元素成為新陣列, 這也是取得子陣列的方法, 其 API :

copyOfRange(T arr, int from, int to)

其中 from 與 to 為要複製的元素索引範圍, 但注意不包含 to 索引元素, 如下列範例所示 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[] i={0,1,2,3,4,5,6,7,8,9};
    int[] i1=Arrays.copyOfRange(i,3,7);
    System.out.println(Arrays.toString(i));    //輸出 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    System.out.println(Arrays.toString(i1));  //輸出 [3, 4, 5, 6]
    }
  }

可見雖指定複製範圍為索引 3~7, 但實際上是只到 6, 也就是 7 的前面. Java 中只要用到索引範圍, 皆是如此, 即不包含終點.

事實上, 陣列本身有一個繼承自 Object 類別的 clone() 方法可用來複製整個陣列, 但是它不能像 copyOfRange() 那樣只複製局部元素, 也就是它無法取得子陣列, 範例如下 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[] i={0,1,2,3,4,5,6,7,8,9};
    int[] i1=i.clone();  //複製陣列
    System.out.println(Arrays.toString(i));    //輸出 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    System.out.println(Arrays.toString(i1));  //輸出 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
  }

這個 clone() 用來複製整個多維陣列或是多維陣列裡面的某個列陣列都很好用, 範例如下 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[][] i={{1,2,3},{4,5,6},{7,8,9}};
    int[][] i1=i.clone();    //複製二維陣列
    int[] i2=i[2].clone();  //複製列陣列
    System.out.println(Arrays.deepToString(i));    //輸出 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    System.out.println(Arrays.deepToString(i1));  //輸出 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    System.out.println(Arrays.toString(i2));           //輸出 [7, 8, 9]
    }
  }

注意, 二維陣列其實是物件陣列 (列陣列之陣列), 因此必須用 deepToString() 才能顯示其元素內容.

還有一個複製陣列的方法, 是利用 System.arraycopy() 方法, 其 API 參見 :

http://docs.oracle.com/javase/7/docs/api/java/lang/System.html

arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

參數 src 為來源陣列, dest 為目標陣列, srcPos 為欲複製之來源陣列起始索引, destPos 為目標陣列複製之起始索引, length 為複製長度. 可見起始索引可以不同, 很有彈性. 但注意, length 不可超過範圍, 否則執行時期將會出現 java.lang.ArrayIndexOutOfBoundsException 錯誤. 範例如下 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[] i={0,1,2,3,4,5,6,7,8,9};
    int[] j={10,11,12,13,14,15,16,17,18,19};
    System.out.println(Arrays.toString(i));  //輸出 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    System.out.println(Arrays.toString(j));  //輸出 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    System.arraycopy(i,2,j,0,5);
    System.out.println(Arrays.toString(j));  //輸出 [2, 3, 4, 5, 6, 15, 16, 17, 18, 19]
    }
  }

可見目標陣列有 5 個元素被取代了.

3. 元素填滿  :

Arrays 還有一個 fill() 方法可以將整個陣列以指定元素填滿, 範例如下 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[] i={0,1,2,3,4,5,6,7,8,9};
    System.out.println(Arrays.toString(i));  //輸出 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Arrays.fill(i,0);
    System.out.println(Arrays.toString(i));  //輸出 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    }
  }

的確會取代原先之元素內容, 但我還想不到這方法何時會用到.

4. 陣列反轉  :

所謂反轉即前軍作後軍, 後軍作前軍也, 索引 0 元素改排在最後面, 最後一個元素變索引 0.

這可以利用 for 迴圈來達成, 範例如下 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    int[] a={0,1,2,3,4,5,6,7,8,9};
    reverse(a);
    System.out.println(Arrays.toString(a));  //輸出 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    }
  public static void reverse(int[] a) {
    for (int i=0; i<a.length; i++) {
      int tmp=a[i];
      a[i]=a[a.length-i-1];
      a[a.length-i-1]=tmp;
      }
    }
  }

此演算法原理就是以陣列中心為軸, 從頭依序處理各索引, 先把該元素放在暫存變數, 然後將對應元素搬過來, 再把暫存變數中的此元素搬到對應元素位置, 完成交換動作. 如果要處理字串陣列, 那就必須方法多載.

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    String[] a={"楊過","小龍女","瑛姑"};
    reverse(a);
    System.out.println(Arrays.toString(a));  //輸出 [瑛姑, 小龍女, 楊過]
    }
  public static void reverse(String[] a) {
    for (int i=0; i<a.length; i++) {
      String tmp=a[i];
      a[i]=a[a.length-i-1];
      a[a.length-i-1]=tmp;
      }
    }
  }

5. 陣列串接 (concate) : 

將兩陣列串接後產生新陣列, 可以用 ArrayList 集合來做, 用 Collections 類別的 addAll() 來串接, 如下所示 :

import java.util.*;
public class mytest {
  public static void main(String[] args) {
    String[] a={"楊過","小龍女","瑛姑"};
    String[] b={"郭靖","黃蓉"};
    String[] c=concate(a,b);
    System.out.println(Arrays.toString(c));  //輸出 [楊過, 小龍女, 瑛姑, 郭靖, 黃蓉]
    }
  public static String[] concate(String[] a,String[] b) {
    ArrayList c=new ArrayList(a.length + b.length);
    Collections.addAll(c, a);
    Collections.addAll(c, b);
    return c.toArray(new String[c.size()]);
    }
  }


參考 :

How do I reverse an int array in Java?
# How to concatenate two arrays in Java?

1 則留言 :

匿名 提到...

public static void reverse(int[] a) {
for (int i=0; i<(a.length/2); i++) {
int tmp=a[i];
a[i]=a[a.length-i-1];
a[a.length-i-1]=tmp;
}
}
}