程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> JAVA編程思想——集合的類型

JAVA編程思想——集合的類型

編輯:JAVA綜合教程

JAVA編程思想——集合的類型


12.JAVA編程思想——集合的類型

標准Java 1.0 和1.1 庫配套提供了非常少的一系列集合類。但對於自己的大多數編程要求,它們基本上都能勝任。Java 1.2 提供的是一套重新設計過的大型集合庫。

1 Vector

Vector 的用法很簡單,大多數時候只需用addElement()插入對象,用elementAt()一次提取一個對象,並用elements()獲得對序列的一個“枚舉”。但仍有其他一系列方法是非常有用的。

Java 標准集合裡包含了toString()方法,所以它們能生成自己的String 表達方式,包括它們容納的對象。

Vector 中,toString()會在Vector 的各個元素中步進和遍歷,並為每個元素調用toString()。假定在想打印出自己類的地址。看起來似乎簡單地引用this 即可(特別是C++程序員有這樣做的傾向):

1.1 代碼如下:

import java.util.*;

publicclass CrashJava {

public String toString() {

return"CrashJava address: " + this +"\n";

}

publicstaticvoidmain(String[]args){

Vector v = new Vector();

for (inti = 0; i < 10;i++)

v.addElement(new CrashJava());

System.out.println(v);

}

}

1.2 輸出

Exceptionin thread "main" java.lang.StackOverflowError

at java.lang.StringBuilder.append(UnknownSource)

atjava.lang.StringBuilder.(Unknown Source)
at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(UnknownSource)

at CrashJava.toString(CrashJava.java:5)

at java.lang.String.valueOf(Unknown Source)

at java.lang.StringBuilder.append(Unknown Source)

若只是簡單地創建一個CrashJava 對象,並將其打印出來,就會得到無窮無盡的一系列違例錯誤。然而,假如將CrashJava對象置入一個Vector,並象這裡演示的那樣打印Vector,就不會出現什麼錯誤提示,甚至連一個違例都不會出現。此時Java 只是簡單地崩潰(但至少它沒有崩潰我的操作系統)。

當我們使用下述語句時:

"CrashJava address: "+ this

編譯器就在一個字串後面發現了一個“+”以及好象並非字串的其他東西,所以它會試圖將this 轉換成一個字串。轉換時調用的是toString(),後者會產生一個遞歸調用。若在一個Vector 內出現這種事情,看起來堆棧就會溢出,同時違例控制機制根本沒有機會作出響應。

若確實想在這種情況下打印出對象的地址,解決方案就是調用Object 的toString 方法。此時就不必加入this,只需使用super.toString()。當然,采取這種做法也有一個前提:我們必須從Object 直接繼承,或者沒有一個父類覆蓋了toString 方法。

2 B i t S e t

BitSet 實際是由“二進制位”構成的一個Vector。如果希望高效率地保存大量“開-關”信息,就應使用BitSet。它只有從尺寸的角度看才有意義;如果希望的高效率的訪問,那麼它的速度會比使用一些固有類型的數組慢一些。

此外,BitSet 的最小長度是一個長整數(Long)的長度:64 位。這意味著假如我們准備保存比這更小的數據,如8 位數據,那麼BitSet 就顯得浪費了。所以最好創建自己的類,用它容納自己的標志位。

在一個普通的Vector 中,隨我們加入越來越多的元素,集合也會自我膨脹。在某種程度上,BitSet也不例外。有時會自行擴展,有時則不然。而且Java 的1.0 版本似乎在這方面做得最糟,它的BitSet 表現十分差強人意(Java1.1 已改正了這個問題)。

2.1 代碼如下:

import java.util.*;

publicclass Bits {

publicstaticvoidmain(String[]args){

Random rand =new Random();

// Take the LSB of nextInt():

bytebt = (byte)rand.nextInt();

BitSet bb =new BitSet();

for (inti = 7; i >= 0;i--)

if (((1 <
bb.set(i);

else

bb.clear(i);

System.out.println("byte value: " +bt);

printBitSet(bb);

shortst = (short)rand.nextInt();

BitSet bs =new BitSet();

for (inti = 15; i >= 0;i--)

if (((1 <
bs.set(i);

else

bs.clear(i);

System.out.println("short value: " +st);

printBitSet(bs);

intit = rand.nextInt();

BitSet bi =new BitSet();

for (inti = 31; i >= 0;i--)

if (((1 <
bi.set(i);

else

bi.clear(i);

System.out.println("int value: " +it);

printBitSet(bi);

// Test bitsets >= 64 bits:

BitSet b127 =new BitSet();

b127.set(127);

System.out.println("set bit 127: " +b127);

BitSet b255 =new BitSet(65);

b255.set(255);

System.out.println("set bit 255: " +b255);

BitSet b1023 =new BitSet(512);

// Without the following, an exception is thrown

// in the Java 1.0 implementation of BitSet:

// b1023.set(1023);

b1023.set(1024);

System.out.println("set bit 1023: " +b1023);

}

 

staticvoidprintBitSet(BitSet b){

System.out.println("bits: " +b);

String bbits =new String();

for (intj = 0; j < b.size(); j++)

bbits += (b.get(j) ?"1" : "0");

System.out.println("bit pattern: " +bbits);

}

} /// :~

2.2 輸出如下:

bytevalue: 96

bits:{5, 6}

bitpattern: 0000011000000000000000000000000000000000000000000000000000000000

shortvalue: 28482

bits:{1, 6, 8, 9, 10, 11, 13, 14}

bitpattern: 0100001011110110000000000000000000000000000000000000000000000000

intvalue: -1121710154

bits:{1, 2, 4, 5, 7, 8, 9, 10, 11, 18, 21, 24, 26, 27, 28, 29, 31}

bitpattern: 0110110111110000001001001011110100000000000000000000000000000000

setbit 127: {127}

setbit 255: {255}

set bit 1023: {1024}

隨機數字生成器用於創建一個隨機的byte、short 和int。每一個都會轉換成BitSet 內相應的位模型。此時一切都很正常,因為BitSet 是64 位的,所以它們都不會造成最終尺寸的增大。但在Java 1.0 中,一旦BitSet 大於64 位,就會出現一些令人迷惑不解的行為。假如我們設置一個只比BitSet 當前分配存儲空間大出1 的一個位,它能夠正常地擴展。但一旦試圖在更高的位置設置位,同時不先接觸邊界,就會得到一個惱人的違例。這正是由於BitSet 在Java 1.0 裡不能正確擴展造成的。本例創建了一個512 位的BitSet。構建器分配的存儲空間是位數的兩倍。所以假如設置位1024 或更高的位,同時沒有先設置位1023,就會在Java1.0 裡得到一個違例。但幸運的是,這個問題已在Java 1.1 得到了改正。如果是為Java 1.0 寫代碼,請盡量避免使用BitSet。

3 S t a c k

Stack 有時也可以稱為“後入先出”(LIFO)集合。換言之,我們在堆棧裡最後“壓入”的東西將是以後第一個“彈出”的。和其他所有Java 集合一樣,我們壓入和彈出的都是“對象”,所以必須對自己彈出的東西進行“造型”。一種很少見的做法是拒絕使用Vector 作為一個Stack 的基本構成元素,而是從Vector 裡“繼承”一個Stack。這樣一來,它就擁有了一個Vector 的所有特征及行為,另外加上一些額外的Stack 行為。很難判斷出設計者到底是明確想這樣做,還是屬於一種固有的設計。

3.1 代碼如下:

import java.util.*;

 

publicclass Stacks {

static String[]months = { "January", "February", "March","April","May","June","July","August","September",

"October","November","December" };

publicstaticvoidmain(String[]args){

Stack stk = new Stack();

for (inti = 0; i < months.length;i++)

stk.push(months[i] +" ");

System.out.println("stk = " +stk);

// Treating a stack as a Vector:

stk.addElement("The last line");

System.out.println("element 5 = " +stk.elementAt(5));

System.out.println("popping elements:");

while (!stk.empty())

System.out.println(stk.pop());

}

}

3.2 輸出如下:

stk= [January , February , March , April , May , June , July , August , September, October , November , December ]

element5 = June

poppingelements:

Thelast line

December

November

October

September

August

July

June

May

April

March

February

January

months 數組的每一行都通過push()繼承進入堆棧,稍後用pop()從堆棧的頂部將其取出。要聲明的一點是,Vector 操作亦可針對Stack 對象進行。這可能是由繼承的特質決定的——Stack“屬於”一種Vector。因此,能對Vector 進行的操作亦可針對Stack 進行,例如elementAt()方法。

4 H a s h t a b l e

Vector 允許我們用一個數字從一系列對象中作出選擇,所以它實際是將數字同對象關聯起來了。但假如我們想根據其他標准選擇一系列對象呢?堆棧就是這樣的一個例子:它的選擇標准是“最後壓入堆棧的東西”。

這種“從一系列對象中選擇”的概念亦可叫作一個“映射”、“字典”或者“關聯數組”。從概念上講,它看起來象一個Vector,但卻不是通過數字來查找對象,而是用另一個對象來查找它們!這通常都屬於一個程序中的重要進程。

在Java 中,這個概念具體反映到抽象類Dictionary 身上。該類的接口是非常直觀的size()告訴我們其中包含了多少元素;isEmpty()判斷是否包含了元素(是則為true);put(Object key, Object value)添加一個值(我們希望的東西),並將其同一個鍵關聯起來(想用於搜索它的東西);get(Object key)獲得與某個鍵對應的值;而remove(ObjectKey)用於從列表中刪除“鍵-值”對。還可以使用枚舉技術:keys()產生對鍵的一個枚舉(Enumeration);而elements()產生對所有值的一個枚舉。這便是一個Dict ionary(字典)的全部。

Dictionary 的實現過程並不麻煩。下面列出一種簡單的方法,它使用了兩個Vector,一個用於容納鍵,另一個用來容納值:

4.1 代碼1如下:

import java.util.*;

publicclass AssocArray extendsDictionary{

privateVector keys = newVector();

privateVector values = newVector();

 

publicintsize() {

returnkeys.size();

}

 

publicbooleanisEmpty() {

returnkeys.isEmpty();

}

 

public Object put(Objectkey, Object value){

keys.addElement(key);

values.addElement(value);

returnkey;

}

 

public Object get(Objectkey) {

intindex = keys.indexOf(key);

// indexOf() Returns -1 if key not found:

if (index == -1)

returnnull;

returnvalues.elementAt(index);

}

 

public Object remove(Objectkey) {

intindex = keys.indexOf(key);

if (index == -1)

returnnull;

keys.removeElementAt(index);

Object returnval =values.elementAt(index);

values.removeElementAt(index);

returnreturnval;

}

 

publicEnumeration keys() {

returnkeys.elements();

}

 

publicEnumeration elements() {

returnvalues.elements();

}

 

// Test it:

publicstaticvoidmain(String[]args){

AssocArray aa =new AssocArray();

for (charc = 'a';c <='z'; c++)

aa.put(String.valueOf(c), String.valueOf(c).toUpperCase());

char[]ca = { 'a','e','i','o','u'};

for (inti = 0; i < ca.length; i++)

System.out.println("Uppercase: "+aa.get(String.valueOf(ca[i])));

}

} /// :~

4.2 輸出1如下:

Uppercase:A

Uppercase:E

Uppercase:I

Uppercase:O

Uppercase:U

在對AssocArray 的定義中,它“擴展”了字典。AssocArray 屬於Dictionary 的一種類型,所以可對其發出與Dictionary 一樣的請求。如果想生成自己的Dictionary,而且就在這裡進行,那麼要做的全部事情只是填充位於Dictionary內的所有方法(而且必須覆蓋所有方法,因為它們——除構建器外——都是抽象的)。

Vector key 和value 通過一個標准索引編號鏈接起來。也就是說,如果用“roof”的一個鍵以及“blue”的一個值調用put()——假定我們准備將一個房子的各部分與它們的油漆顏色關聯起來,而且AssocArray 裡已有100 個元素,那麼“roof”就會有101 個鍵元素,而“blue”有101 個值元素。而且要注意一下get(),假如我們作為鍵傳遞“roof”,它就會產生與keys.index.Of()的索引編號,然後用那個索引編號生成相關的值矢量內的值。

main()中進行的測試是非常簡單的;將小寫字符轉換成大寫字符,這顯然可用更有效的方式進行。向我們揭示出了AssocArray 的強大功能。

標准Java 庫只包含Dictionary 的一個變種,名為Hashtable(散列表)。Java 的散列表具有與AssocArray 相同的接口(因為兩者都是從Dictionary 繼承來的)。但有一個方面卻反映出了差別:執行效率。若仔細想想必須為一個get()做的事情,就會發現在一個Vector 裡搜索鍵的速度要慢得多。但此時用散列表卻可以加快不少速度。不必用冗長的線性搜索技術來查找一個鍵,而是用一個特殊的值,名為“散列碼”。散列碼可以獲取對象中的信息,然後將其轉換成那個對象“相對唯一”的整數(int)。所有對象都有一個散列碼,而hashCode()是根類Object 的一個方法。Hashtable 獲取對象的hashCode(),然後用它快速查找鍵。這樣可使性能得到大幅度提升。

現在只需要知道散列表是一種快速的“字典”(Dictionary)即可,而字典是一種非常有用的工具。

4.3 代碼2

應用散列表的一個例子,可考慮用一個程序來檢驗Java 的Math.random() 方法的隨機性到底如何。在理想情況下,它應該產生一系列完美的隨機分布數字。但為了驗證這一點,我們需要生成數量眾多的隨機數字,然後計算落在不同范圍內的數字多少。散列表可以極大簡化這一工作,因為它能將對象同對象關聯起來

importjava.util.*;

classCounter {

inti = 1;

 

publicString toString() {

returnInteger.toString(i);

}

}

 

classStatistics {

publicstaticvoidmain(String[]args) {

Hashtable ht =newHashtable();

for (inti = 0; i < 10000;i++) {

// Produce anumber between 0 and 20:

Integerr =new Integer((int) (Math.random()* 20));

if (ht.containsKey(r))

((Counter)ht.get(r)).i++;

else

ht.put(r,newCounter());

}

System.out.println(ht);

}

} /// :~

4.4 輸出如下:

{19=489,18=510, 17=478, 16=509, 15=515, 14=477, 13=479, 12=523, 11=504, 10=501, 9=522,8=489, 7=508, 6=481, 5=505, 4=502, 3=542, 2=481, 1=528, 0=457}

在main()中,每次產生一個隨機數字,它都會封裝到一個Integer 對象裡,使句柄能夠隨同散列表一起使用(不可對一個集合使用基本數據類型,只能使用對象句柄)。containKey()方法檢查這個鍵是否已經在集合裡(也就是說,那個數字以前發現過嗎?)若已在集合裡,則get()方法獲得那個鍵關聯的值,此時是一個Counter(計數器)對象。計數器內的值i 隨後會增加1,表明這個特定的隨機數字又出現了一次。

假如鍵以前尚未發現過,那麼方法put()仍然會在散列表內置入一個新的“鍵-值”對。在創建之初,Counter 會自己的變量i 自動初始化為1,它標志著該隨機數字的第一次出現。

為顯示散列表,只需把它簡單地打印出來即可。Hashtable toString()方法能遍歷所有鍵-值對,並為每一對都調用toString()。Integer toString() 是事先定義好的,可看到計數器使用的toString。

或許會對Counter 類是否必要感到疑惑,它看起來似乎根本沒有封裝類Integer 的功能。為什麼不用int 或Integer 呢?事實上,由於所有集合能容納的僅有對象句柄,所以根本不可以使用整數。學過集合後,封裝類的概念對大家來說就可能更容易理解了,因為不可以將任何基本數據類型置入集合裡。然而,我們對Java 封裝器能做的唯一事情就是將其初始化成一個特定的值,然後讀取那個值。也就是說,一旦封裝器對象已經創建,就沒有辦法改變一個值。這使得Integer 封裝器對解決我們的問題毫無意義,所以不得不創建一個新類,用它來滿足自己的要求。

4.5 代碼3創建關鍵類

用一個標准庫的類(Integer)作為Hashtable 的一個鍵使用。作為一個鍵,它能很好地工作,因為它已經具備正確運行的所有條件。但在使用散列表的時候,一旦我們創建自己的類作為鍵使

用,就會遇到一個很常見的問題。例如,假設一套天氣預報系統將Groundhog(土拔鼠)對象匹配成Prediction(預報)。這看起來非常直觀:我們創建兩個類,然後將Groundhog作為鍵使用,而將Prediction 作為值使用。如下所示:

importjava.util.*;

classGroundhog {

intghNumber;

Groundhog(intn) {

ghNumber =n;

}

}

classPrediction {

booleanshadow = Math.random() > 0.5;

publicString toString() {

if (shadow)

return"Six more weeks of Winter!";

else

return"Early Spring!";

}

}

publicclass SpringDetector {

publicstaticvoidmain(String[]args) {

Hashtable ht =newHashtable();

for (inti = 0; i < 10;i++)

ht.put(new Groundhog(i),new Prediction());

System.out.println("ht = " + ht +"\n");

System.out.println("Looking up prediction for groundhog #3:");

Groundhoggh =new Groundhog(3);

if (ht.containsKey(gh))

System.out.println((Prediction)ht.get(gh));

}

}

4.6 輸出3

ht= {Groundhog@7852e922=Early Spring!, Groundhog@5c647e05=Early Spring!,Groundhog@75b84c92=Early Spring!, Groundhog@33909752=Early Spring!,Groundhog@42a57993=Six more weeks of Winter!, Groundhog@55f96302=Six more weeksof Winter!, Groundhog@4e25154f=Six more weeks of Winter!,Groundhog@70dea4e=Early Spring!, Groundhog@3d4eac69=Early Spring!,Groundhog@6d06d69c=Six more weeks of Winter!}

 

Lookingup prediction for groundhog #3:

每個Groundhog 都具有一個標識號碼,所以赤了在散列表中查找一個Prediction,只需指示它“告訴我與Groundhog 號碼3 相關的Prediction”。Prediction類包含了一個布爾值,用Math.random()進行初始化,以及一個toString()為我們解釋結果。在main()中,用Groundhog 以及與它們相關的Prediction 填充一個散列表。散列表被打印出來,以便我們看到它們確實已被填充。隨後,用標識號碼為3 的一個Groundhog 查找與Groundhog#3 對應的預報。

看起來似乎非常簡單,但實際是不可行的。問題在於Groundhog 是從通用的Object 根類繼承的(若當初未指定基礎類,則所有類最終都是從Object 繼承的)。事實上是用Object 的hashCode()方法生成每個對象的散列碼,而且默認情況下只使用它的對象的地址。所以,Groundhog(3)的第一個實例並不會產生與Groundhog(3)第二個實例相等的散列碼。

用代碼4進行檢索。除了正確地覆蓋hashCode()。再做另一件事情:覆蓋也屬於Object 一部分的equals()。當散列表試圖判斷我們的鍵是否等於表內的某個鍵時,就會用到這個方法。同樣地,默認的Object.equals()只是簡單地比較對象地址,所以一個Groundhog(3)並不等於另一個Groundhog(3)。

因此,為了在散列表中將自己的類作為鍵使用,必須同時覆蓋hashCode()和equals(),如下

4.7 代碼4

import java.util.*;

 

class Groundhog2 {

intghNumber;

 

Groundhog2(intn) {

ghNumber =n;

}

 

publicinthashCode() {

returnghNumber;

}

 

publicbooleanequals(Object o) {

return (oinstanceof Groundhog2) && (ghNumber == ((Groundhog2)o).ghNumber);

}

}

 

publicclass SpringDetector2 {

publicstaticvoidmain(String[]args){

Hashtable ht = new Hashtable();

for (inti = 0; i < 10;i++)

ht.put(new Groundhog2(i),new Prediction());

System.out.println("ht = " +ht + "\n");

System.out.println("Looking up prediction for groundhog #3:");

Groundhog2 gh =new Groundhog2(3);

if (ht.containsKey(gh))

System.out.println((Prediction)ht.get(gh));

}

}

4.8 輸出4

ht= {Groundhog2@9=Six more weeks of Winter!, Groundhog2@8=Six more weeks ofWinter!, Groundhog2@7=Six more weeks of Winter!, Groundhog2@6=Six more weeks ofWinter!, Groundhog2@5=Early Spring!, Groundhog2@4=Six more weeks of Winter!,Groundhog2@3=Six more weeks of Winter!, Groundhog2@2=Six more weeks of Winter!,Groundhog2@1=Six more weeks of Winter!, Groundhog2@0=Early Spring!}

 

Lookingup prediction for groundhog #3:

Sixmore weeks of Winter!

這段代碼使用了來自前一個例子的Prediction,所以SpringDetector.java 必須首先編譯,否則就會在試圖編譯SpringDetector2.java 時得到一個編譯期錯誤。

Groundhog2.hashCode()將土拔鼠號碼作為一個標識符返回(在這個例子中,程序員需要保證沒有兩個土拔鼠用同樣的ID 號碼並存)。為了返回一個獨一無二的標識符,並不需要hashCode(),equals()方法必須能夠嚴格判斷兩個對象是否相等。

equals()方法要進行兩種檢查:檢查對象是否為null;若不為null ,則繼續檢查是否為Groundhog2 的一個實例(要用到instanceof 關鍵字)。即使為了繼續執行equals(),它也應該是一個Groundhog2。這種比較建立在實際ghNumber 的基礎上。這一次一旦我們運行程序,就

會看到它終於產生了正確的輸出(許多Java 庫的類都覆蓋了hashcode() 和equals()方法,以便與自己提供的內容適應)。

在以前筆記中,我們使用了一個名為Properties(屬性)的Hashtable 類型。在那個例子中,下述程序行:

Properties p =System.getProperties();

p.list(System.out);

調用了一個名為getProperties()的static 方法,用於獲得一個特殊的Properties 對象,對系統的某些特征進行描述。list()屬於Properties 的一個方法,可將內容發給我們選擇的任何流式輸出。也有一個save()方法,可用它將屬性列表寫入一個文件,以便日後用load()方法讀取。盡管Properties 類是從Hashtable 繼承的,但它也包含了一個散列表,用於容納“默認”屬性的列表。所以假如沒有在主列表裡找到一個屬性,就會自動搜索默認屬性。

Properties 類在Java 庫的用戶文檔中,往往可以找到更多、更詳細的說明。

5 再論枚舉器

演示Enumeration(枚舉)的真正威力:將穿越一個序列的操作與那個序列的基礎結構分隔開。PrintData 類用一個Enumeration 在一個序列中移動,並為每個對象都調用toString()方法。此時創建了兩個不同類型的集合:一個Vector 和一個Hashtable。並且在它們裡面分別填充Mouse 和Hamster 對象(先編譯HamsterMaze.java 和WorksAnyway.java,否則下面的程序不能編譯)。由於Enumeration 隱藏了基層集合的結構,所以PrintData 不知道或者不關心Enumeration 來自於什麼類型的集合:

5.1 代碼

WorksAnyway.java:

importjava.util.*;

 

classMouse {

privateintmouseNumber;

 

Mouse(inti) {

mouseNumber =i;

}

 

// Magic method:

publicString toString() {

return"This is Mouse #" +mouseNumber;

}

 

voidprint(Stringmsg) {

if (msg !=null)

System.out.println(msg);

System.out.println("Mouse number " + mouseNumber);

}

}

 

classMouseTrap {

staticvoid caughtYa(Objectm) {

Mousemouse =(Mouse)m;// Cast fromObject

mouse.print("Caught one!");

}

}

 

publicclass WorksAnyway {

publicstaticvoidmain(String[]args) {

Vector mice =newVector();

for (inti = 0; i < 3;i++)

mice.addElement(new Mouse(i));

for (inti = 0; i 
// No castnecessary, automatic call

// toObject.toString():

System.out.println("Free mouse: " + mice.elementAt(i));

MouseTrap.caughtYa(mice.elementAt(i));

}

}

}

Enumerators2.java

import java.util.*;

class PrintData {

staticvoidprint(Enumeration e) {

while (e.hasMoreElements())

System.out.println(e.nextElement().toString());

}

}

 

class Enumerators2 {

publicstaticvoidmain(String[]args){

Vector v = new Vector();

for (inti = 0; i < 5;i++)

v.addElement(new Mouse(i));

Hashtable h = new Hashtable();

for (inti = 0; i < 5;i++)

h.put(new Integer(i),new Hamster(i));

System.out.println("Vector");

PrintData.print(v.elements());

System.out.println("Hashtable");

PrintData.print(h.elements());

}

} /// :~

5.2 輸出

Vector

Thisis Mouse #0

Thisis Mouse #1

Thisis Mouse #2

Thisis Mouse #3

Thisis Mouse #4

Hashtable

Thisis Hamster #4

Thisis Hamster #3

Thisis Hamster #2

Thisis Hamster #1

Thisis Hamster #0

注意PrintData.print()利用了這些集合中的對象屬於Object 類這一事實,所以它調用了toString()。但在解決自己的實際問題時,經常都要保證自己的Enumeration 穿越某種特定類型的集合。例如,可能要求集合中的所有元素都是一個Shape(幾何形狀),並含有draw()方法。若出現這種情況,必須從Enumeration.nextElement()返回的Object 進行下溯造型,以便產生一個Shape。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved