SlideShare uma empresa Scribd logo
1 de 187
第 18 章

集合與泛型
本投影片(下稱教用資源)僅授權給採用教用資源相關之旗標書籍為教科書之授課老師(下稱老師)專用,老
師為教學使用之目的,得摘錄、編輯、重製教用資源(但使用量不得超過各該教用資源內容之 80% )以製作為
輔助教學之教學投影片,並於授課時搭配旗標書籍公開播放,但不得為網際網路公開傳輸之遠距教學、網路教
學等之使用;除此之外,老師不得再授權予任何第三人使用,並不得將依此授權所製作之教學投影片之相關著
作物移作他用。
著作權所有 © 旗標出版股份有限公司
學習目標
 學習使用 Collection 相關類別與介面
 活用集合的排序功能
 管理集合與陣列的 Collections 和 Arrays 類別
 瞭解泛型的意義
 活用泛型集合
 學習泛型的通用型別
 自訂泛型類別與泛型方法
前言
 在 java.util 套件中 , 還有一個相當重要的集合
(Collection) 類別群 , 其中包含了可以儲存、處理各
種資料結構的類別 , 例如
ArrayList 、 LinkList 、 TreeSet 等 , 對於在程式中
處理資料時很有幫助。
 而當我們在集合中進行搜尋或排序時 , 還會使用到物
件繼承自 Object 類別的 equals() 及 hashcode()
方法 , 以上這些在本章中都會詳細介紹。
前言
 此外 , 自 Java 5 才有的泛型 (Generic Type) 功
能 , 主要是用來提升型別檢查的安全性。
 除了可使用在現有的集合類別之外 ( 例如宣告一個
只能儲存 String 物件的集合 ), 我們也可用它來定
義自己的泛型類別或泛型函式 , 讓同樣的程式碼能
安全地處理各種不同型別的資料。
18-1 集合 (Collection)
 在撰寫程式時 , 常需要處理一群同性質資料的集合 ,
為了方便程式處理這樣的集合 , Java API 特別在
java.util 套件中提供了一組介面和類別 , 讓我們可建
立這類資料集合的物件 , 這個物件就稱之為
Collection ( 集合物件 ) 。
 為了使這些集合類別有一致性 , Java 特別將
Collection 相關介面設計成一完整的 Collections
Framework 架構。
集合 (Collection)
 以下我們先簡介 Java 的 Collections Framework,
然後再分類說明各集合類別的用法。
▪ Collections Framework 為 SCJP 考試重點之一 , 請
務必熟悉這個主題。
18-1-1 Collections Framework 簡介
 當程式需要處理集合式的資料時 ( 例如要處理大量的
學生資料 ) , 就可利用 Collections Framework 中的
集合類別來建立代表學生資料的集合物件 , 接著即可
利用集合類別提供的現成方法 , 來處理集合物件中的
每一筆資料。
Collections Framework 簡介
 Collections Framework 的核心 , 就是各集合相關介
面 , 這些介面分成以下 2 個體系:
Collections Framework 簡介
 Collection 介面:身為最上層的介面 , Collection 介面
具有相當的彈性 , 它可代表任何物件集合 , 這些物件可
以是有次序性或無次序性、有重複或沒有重複均可。此
介面定義了基本的集合操作方法 , 像是新增 / 移除集合
中的元素、將另一個集合新增進來或移除、將集合物件
轉成陣列的形式等等。
 Set 及 SortedSet 介面: Set 代表的是沒有重複元素
的集合 , 和 Collection 介面一樣 , Set 介面也提供基本
的元素新增 / 移除等方法。 SortedSet 介面則是表示集
合中的元素會自動排序 , 因此 SortedSet 介面也多定義
了幾個與次序相關的方法 , 例如可傳回最前面或最後面
Collections Framework 簡介
 List 介面: List 代表有次序性 , 但元素可能有重複
的集合。 List 的元素有類似於陣列元素的索引碼 ,
因此可透過 List 介面的方法 , 快速存取到指定的元
素、或某範圍內的元素。
 Queue 介面:和 List 有點類似 , 也是有次序性及可
重複的集合 , 而其特色則是著重於佇列式的存取 , 也
就是加入元素時會加到最後面 , 而取出元素時則由最
前面取出。這就是所謂『先進先出』 (First In First
Out) 的存取方式 , 稍後會再介紹。
Collections Framework 簡介
 Map 及 SortedMap 介面: Map 是個有『鍵 值』 (key-value) 對應關係的元素集合 , 例如每個學
號對應到一名學生 , 就是一種 Map 對應 , 其中 key
的值 ( 例如學號 ) 不能有重複。至於 SortedMap 介
面 , 則是代表會自動排序的 Map 集合。
 有了基本的認識後 , 以下再分別介紹每個介面 , 以及
java.util 套件中實作該介面的類別。
18-1-2 Collection 介面與相關類別
 如前所述 , Collection 介面提供了基本的集合操作方
法 , Set 與 List 類的集合類別都有實作這些方法 ,
以便進行相關操作。
 在實際使用各集合類別之前 , 我們先來認識一下
Collection 介面提供的基本方法 , 稍後介紹各集合類
別後 , 即可直接使用這些方法。
 Collection 介面提供的操作方法可分為三類:基本管
理、大量元素的管理、及陣列的轉換。
基本管理方法
 基本管理的方法有以下幾個:

 除了 iterator() 方法外 , 其它幾個方法應該都很直覺 ,
不必多加說明 , 至於 Iterator 介面與 iterator() 方法會
在本章稍後再加說明。
▪ 請注意 , 只有『 參照型別』 的物件才能放入集合中。
基本型別的資料 (int 、 long 、 double 等 ) 則須先封
箱為基本類別 (Integer 、 Long 、 Double 等 ) 的物
件 , 然後才能存入集合中。
大量元素管理方法
 為方便一次進行大量元素的增刪動作 , Collection 介
面提供以下幾個實用的方法:
陣列轉換
 如果遇到需用陣列的方式來操作集合的情況 , 就可用
下列方法將集合內容存到一個全新的陣列中 , 然後用
該陣列進行處理。

 請注意:產生的新陣列除了其元素值與集合元素相同
外 , 兩者並無其它關係 , 例如修改陣列元素值 , 並不
會影響到集合元素。
Collections 類別
 在 java.util 套件中 , 還有個名為 Collections 的類別。
 它與 Collection 介面並無『直接』關係 , 此類別的主要
功能 , 是提供一些 static 方法 , 方便我們對集合物件進
行一些處理 , 雖然此類別的名稱是 Collections, 不過其
意思泛指所有的集合物件 , 而不限於只有實作 Collection
介面的類別 , 所以它所提供的部份方法也能用於 Map
類的集合物件。
 Collections 類別提供的『工具』包括:搜尋、找出集合
中最大或最小的元素、將 List 集合排序或打亂次序等等
。
 稍後我們會示範一些 Collections 類別提供的 static 方
法的用法。
18-1-3 重新定義 equals() 和 hashcode() 方法
 在操作集合中的元素時 , 經常會需要比較二個物件是
否相等 , 例如使用前述的 contains(Object o) 來檢查
集合中是否包含 o 物件 , 這時就會使用物件繼承自
Object 類別的 equals() 來進行比較。
 另外 , 為了提升在集合中尋找元素的效率 , 有些集合
類別會使用雜湊表 (Hash table) 的方式來存取元素 ,
例如 HashSet 、 HashMap 等 ( 凡是名稱中包含
Hash 的類別都是 ) 。
 這些類別在存取元素時 , 會呼叫物件繼承自 Object
類別的 hashcode() 來取得其雜湊碼 (Hash code),
然後以雜湊碼做為儲存或尋找元素的依據。
重新定義 equals() 和 hashcode() 方法
 不過 Object 的 equals() 預設是以 == 做比較 , 而
hashcode() 則會傳回每個物件都不同的唯一整數序
號 ( 通常是由物件的位址轉換而來 ), 這二種方法都
只有當變數參照到「同一物件」時才會相等 , 因此都
不適用於集合 , 而必須重新定義 , 例如。
重新定義 equals() 和 hashcode() 方法
重新定義 equals() 和 hashcode() 方法
重新定義 equals() 和 hashcode() 方法
 其實二物件是否相等 , 應由我們自行定義要如何比較
, 例如每個學生的學號都是唯一的 , 因此可用學號
(id) 做為比較的依據。
 底下就來重新定義這二個方法:
重新定義 equals() 和 hashcode() 方法
重新定義 equals() 和 hashcode() 方法
 以上除了 equals() 及 hashCode() 外 , 也順便實作
了 toString() 方法。
 請注意 Object 類別的這 3 個方法均為 public, 而且
equals() 的參數必須定義為 Object 型別 ( 若改為
Student 則變成多重定義 ), 這樣才能正確地重新定
義。
▪ Java 內建的基本類別 (Integer 、 Long...) 、 String
等 , 都已經重新定義好繼承自 Object 的相關方法了 ,
所以我們可以直接使用。
關於雜湊碼的補充說 明
 前面 Student 類別的識別資料 ( 學號 ) 剛好為 int, 所
以可以很方便地做為雜湊碼。
 但如果識別資料是文字或其他較複雜的資料 , 例如學號
的格式為 "RA001" ( 其中的 RA 可能代表不同的年級
或班別 ) , 或是像某些軟體的序號為一堆文字
"SE23q0Xt9z45Ke81Puig" 。
 序號越長 , 在搜尋資料時效率將越差 , 因為要比對的資
料會越多。
 正因為這樣 , 所以才需要使用雜湊碼 , 也就是將一長串
的序號簡化為一個較短的整數 , 那麼用整數來搜尋時效
率就會快很多。
關於雜湊碼的補充說 明
 至於轉換為整數的方法 , 其實怎麼轉都可以 , 只要符
合以下 3 個原則:

1. 同一個物件 , 每次傳回的雜湊碼都必須相同。例如甲學生
的雜湊碼每次傳回都要一樣 , 例如 4502 。 ( 但下一次執
行程式時 , 則允許和這一次的不同。 )
2. 內容相等的物件 ( 就是用 equals() 比較為相等 ), 傳回的
雜湊碼也必須相等。反之 , 雜湊碼不相等時 , 其物件也必
須不相等。例如建立了 2 個甲學生物件 , 則其雜湊碼必
須都是 4502 ;如果某人的雜湊碼不是 4502, 那麼肯定
不是甲學生。
3. 內容不相等的物件 , 傳回的雜湊碼可以相等 , 也可以不等。
換句話說 , 雜湊碼相等時 , 其物件未必相等。例如甲和乙
的雜湊碼可以都是 4502 。
關於雜湊碼的補充說 明
 其實雜湊表的搜尋原理 , 就是先用雜湊碼去搜尋 ,
如果找到多個符合的物件 , 則再用 equals() 去找。
 由此可知 , 當不同物件傳回的雜湊碼都不相同時 ,
搜尋的效率最高;反之如果傳回的雜湊碼都相同 ,
則搜尋效率最差 , 因為還必須再用 equals() 去一一
比對 , 這樣反而失去了使用雜湊碼的意義。
關於雜湊碼的補充說 明
 所以我們在設計 hashCode() 時 , 應儘量讓每個物
件都不同 , 或是減少相同雜湊碼的物件數 , 以提升
效率。
 不過 , 如果集合中的元素不多 , 那也就不必要求這
麼高了。
▪ 即使所有物件都傳回相同的雜湊碼 , 該程式也可以是
正確的、合法的、甚至是適當的 ( 視需要而定 ) 。不
過正確的程式 ( 可以正常編譯、執行 ), 不一定是好
的程式。
18-1-4 Set 介面與相關類別
 Set 介面代表的是元素內容都不重複的集合 , 換言之在
Set 類型的集合物件中 , 每個元素都是不同的。
 Set 介面並未提供新的方法 , 所以其方法都是繼承自
Collection 介面。
 由於『元素內容都不重複』 , 所以無法用
add() 、 addAll() 等方法將重複的元素加入 Set 類型
的集合物件中。
 實作 Set 介面的類別為抽象類別 Abs tractSet , 它有
三個衍生類別: HashSet 、 LinkedHashSet 、
TreeSet 。
 此外前面提過有個繼承 Set 介面的 SortedSet 介面 ,
TreeSet 即實作這個 SortedSet 介面。這些類別與
Set 介面與相關類別

▪ 凡是名稱中有 Hash 的集合類別 , 都
有使用前述的雜湊表來提升存取效率。
因此任何要存入 其中的物件 , 都必須
已實作繼承自 Object 的 hashCode()
方法。
Set 介面與相關類別
 各集合類別的建構方法都和陣列一樣可指定初始的大
小 , 例如以下就是建立可存放 10 個元素的 TreeSet
類別物件:
 上列敘述設定的只是『初始』大小。稍後隨程式運作
需要 , 有更多元素加入集合時 , 集合的大小也會隨之
擴大 , 不像陣列一開始設定大小後就不能變動。
 本章後續介紹的其它類集合類別 , 也都有相同的建構
方法可用 , 稍後就不再加說明。
建立集合物件的新語法
 請注意 , 如果使用 5.0 版之後的 JDK, 上列敘述雖
仍可編譯成功 , 但編譯器會特別提出說明 (Note) 。
 這是因為 JDK 5.0 新增了 Generics ( 泛型 ) 的功能
, 讓我們在建立集合物件時 , 可一併標明此集合將存
放何種類型的物件。
 例如要存放字串的話 , 就要將程式寫成:
建立集合物件的新語法
 請本節的程式仍採 5.0 以前的寫法 , 也就是不替集
合加上型別的宣告 , 以便將注意力放在集合上。
 下一節則會為您詳細介紹泛型的各種應用。
Set 介面與相關類別
 以下就來介紹 HashSet 、 LinkedHashSet 、和
TreeSet 類別的用法。
HashSet
 HashSet 適用於任何希望元素不重複 , 但不在意其
次序性的集合。
 要建立 HashSet 物件可先建立空的 HashSet 物件
, 再自行用 add() 方法將物件加入 , 或是用另一個集
合物件為參數呼叫建構方法。
 除了繼承自 Collection 介面的各方法外 , HashSet
並未定義其它新的方法。
HashSet
 以下範例就是用 HashSet 設計一個文字接龍遊戲 ,
程式會建立一個 HashSet 物件 , 而使用者每輸入一
個新詞 , 就會加到 HashSet 物件中。
 由於 HashSet 中不能有重複的元素 , 所以使用者輸
入重複的詞 , 就無法被加到 HashSet 中 , 遊戲即結
束。
 程式內容如下。
HashSet
HashSet
HashSet

1. 第 15 行建立一個空的 HashSet 物件 words 。
HashSet
2. 第 17 〜 24 行以 while 迴圈的方式執行將使用者
輸入字串加入集合 , 以及請使用者輸入下個字串的
動作。為簡明起見 , 本範例未如真的文字接龍一樣 ,
檢查新輸入的字首與先前的字尾是否一致。
3. 第 18 〜 21 行以 if 執行 add() 方法將字串加入集
合 , 若 add() 傳回 false, 表示集合中已有相同內容
的物件 (Set 類型集合不允許重複的元素 ) 。此時就
會用 break 敘述跳出迴圈。
4. 第 27 行直接將 words 的內容輸出 , 由輸出結果可
發現 , 集合類別的 toString() 方法會自動在前後加
上一對方括號。
HashSet
 從上列程式最後輸出的結果可發現 , HashSet 物件中
元素的順序和加入時的順序並不相同 , 這即是
HashSet 『無次序性』的特性。
 如果希望元素的次序能保持一致 , 則可改用
LinkedHashSet 。
LinkedHashSet
 LinkedHashSet 是 HashSet 的子類別 , 兩者的特性
也類似 , 只不過 LinkedHashSet 會讓集合物件中各
元素維持加入時的順序。
 我們直接將上述的範例程式改用 LinkedHashSet 類
別即可發現:
LinkedHashSet
LinkedHashSet
LinkedHashSet
 請執行結果可發現 , 改用 LinkedHashSet 來建立集
合物件後 , 元素存放的順序就和加入時一樣。
 這個程式最後面 , 我們故意用 toArray() 方法將集合
內容轉換成字串陣列 , 再以迴圈輸出所有字串 , 讓讀
者認識一下 toArray() 方法的用法。
TreeSet
 TreeSet 和前 2 個類別最大的差異 , 在於 TreeSet
實作了具有排序功能的 SortedSet 介面 , 一旦物件加
入集合就會自動排序。
 也因為這個特點 , 此類別多了幾個和次序有關的方法
可用:
LinkedHashSet
▪ 如果集合中的元素是基本型別 , 例如 int, 那麼 first()
及 last() 傳回的 Object 資料不可用 int 強制轉型
( 因為 Object 只能轉型為參考型別 ) 。此時必須先強
制轉型為 Integer , 然後就會自動拆箱 (Unboxing) 為
int 。

 如果元素是 String 或數值類的基本型別 , 那麼就會
依照字母或數值由小到大排序 ( 稱為自然順序 ) 。
 但若是自訂的類別 , 則必須由我們自行定義排序方式
, 相關方法請參見 18-1-10 節。
LinkedHashSet
LinkedHashSet
1. 第 3 行建立一個字串陣列 , 並在第 6 行以 Arrays
類別的 asList() 將之轉換為 List 集合 , 然後加入
ts 集合中。
2. 由程式輸出的結果 , 可看出「空白 < - < 0~9 < A~Z
< a~z < 中文字」 , 請記住這個自然順序。
 要特別注意的是 xxxSet() 方法傳回的子集合 , 仍
『屬於』原始的集合物件 , 所以若改變子集合的內
容 , 原集合的內容也會改變 , 請參考以下的程式範
例。
LinkedHashSet
LinkedHashSet
LinkedHashSet
1. 第 8 〜 10 行建立一個新的 TreeSet 物件 , 隨後
以迴圈將 10 到 100 間 10 的倍數之整數物件加
到其中。請注意 , 在第 10 行加入元素時 ,
add(new Integer(i*10)) 也可寫成 add(i*10), 因為
int 在必要時會自動封箱 (Autoboxing) 為 Integer
物件。
2. 第 14 行取得小於 50 的元素所形成的子集合。並
於第 16 行用 clear() 方法將之清除。
3. 第 11 、 18 行分別輸出移除子集合前後的母集合
大小 , 以供比較。
LinkedHashSet
 如執行結果所示 , 當我們移除子集合後 , 母集合的元
素也跟著變少了 , 這就表示 xxxSet() 方法傳回的子
集合 , 仍是原始集合物件的一部份。
使用子集合的注意事項
 前面使用 headSet 、 subSet 、 tailSet 所傳回的子
集合稱為後備集合 (Backed Collection) 。
 當我們異動的元素落在後備集合的範圍時 , 原始集合
與後備集合都會同時被異動 , 而當新增一個超出範圍
的元素到後備集合中時 , 則會丟出例外!例如在前面
程式的後面加入:
18-1-5 List 介面與相關類別
 List 適用於有次序性、但元素可能重複的集合。 List
集合在使用上和陣列有些類似 , 因為 List 集合中的
元素 , 也都可透過索引 (index) 來存取。
 因此 List 介面定義了一些與索引有關的方法 , 例如
除了繼承自 Collection 介面的 add() 、 remove()
的新增移除元素方法 , 也增加了可指定索引值來新增
或移除元素的方法。
List 介面與相關類別
List 介面與相關類別
 上列的 add() 和 set() 方法感覺上都是將第 index
元素設為物件 element, 但其實兩者的意義差別很大
。
 set() 是將原位置上的物件『取代』掉;但 add() 則
是在指定位置加入新物件 , 而原物件則是往後移。如
下圖:
List 介面與相關類別
 在 java.util 套件中實作 List 界面的只有
AbstractList 抽象類別 , 其衍生類別則有
AbstractSquentialList 、 ArrayList 、 LinkedList 等 ,
其關係如下:
ArrayList
 我們可將 ArrayList 看成是個伸縮自如的陣列 , 原本
的陣列在宣告之後 , 元素的數量就固定了 , 不能再增
加元素的個數 , 此外若要在陣列中插入一個元素 , 並
讓原來的元素都向後移一個位置 , 也必須自已做相關
處理 , 相當不便 , 但使用 ArrayList 就不會這麼麻煩
了。
 然而不斷地變動大小、或配置過多未用的空間對程式
/ 系統的效能也有些負面影響 , 因此 ArrayList 也提
供了 2 個與使用空間有關的管理方法:
ArrayList
 以下的範例程式簡單示範 ArrayList 的基本用法:
ArrayList
ArrayList

1. 第 8 行建立一個新的 ArrayList 物件 , 隨後以迴圈將字元陣
列的內容加到其中。
2. 第 12 、 13 分別是先輸出集合目前大小 , 再輸出所有的元
素。
3. 第 15 〜 17 行用 add() 方法在指定位置加入新元素 , 舊元
素則向後移。
4. 第 21 、 22 行是故意換用迴圈呼叫 Collection 類別的 get()
ArrayList
 從上列程式第 1 次的執行結果可發現 , 在不指定索
引的情況下 , 以 add() 方法加入的元素都是依序加
到集合中『最後面』 , 所以一開始加入的元素排列順
序 , 和陣列的內容相同。
 但之後用另一個 add() 方法 , 同時指定了加入元素
的索引 , 元素就只會加到我們指定的位置 , 而原位置
及其後的元素都依序向後移一位。
ArrayList
 當您想使用陣列來處理資料 , 但在程式執行過程中可
能需移除或增加陣列元素 , 就可先用 ArrayList 來建
立集合 , 待一切底定、元素數量都不會更動後 , 再呼
叫繼承自 Collection 介面的 toArray() 方法將其轉
成陣列。
LinkedList
 LinkedList 是另一類型的 List 類別 , LinkedList 的
特點是它提供了一組專門處理集合中第一個、最後一
個元素的方法:
LinkedList
 由於上述的特性 , LinkedList 很適合用來實作兩種基
本的『資料結構』 (Data Structure) :即堆疊及佇列
。
 所謂堆疊 (stack) 是指一種後進先出 (LIFO, Last In
First Out) 的資料結構 , 加入此結構 ( 集合 ) 的物件
要被取出時 , 必須等其它比它後加入的物件全部被拿
出來後 , 才能將它拿出來。
LinkedList
 例如一端封閉的網球收納筒 , 就是一種堆疊結構:
LinkedList
 至於佇列 (queue) 則是一種『先進先出』 (FIFO)
的資料結構 , 像日常生活中常見的排隊購物 , 此隊伍
就是個先進先出的佇列:
LinkedList
 對資料結構有興趣者 , 可參照其它相關主題的書籍。
 初學程式語言者可先記住 , java.util 套件已事先設計
好 LinkedList 類別 , 當您日後遇到需使用堆疊或佇
列的資料結構的場合 , 就可直接用 LinkedList 來設
計程式 , 不必自行從頭設計資料結構。
 以下就是一個簡單的 LinkedList 應用範例。
LinkedList
LinkedList
LinkedList
LinkedList
1. 第 15 行建立一個新的 LinkedList 物件。
2. 第 16 〜 26 行的 for 迴圈逐一檢查輸入字串中所
含的左右括號。
3. 第 17 〜 18 行讀到左括號時就用 addFirst() 方法加
入一個新元素。
4. 第 19 〜 26 行讀到右括號時就用 removeFirst() 方
法移除一個元素。
LinkedList
5. 第 23 〜 26 行 , 若發生
NoSuchElementException 例外 , 表示集合中已無
元素 , 但仍有右括號 , 表示右括號是多出來的 , 或
先前漏打了一個左括號。
▪

LinkList 類別同時實作了 List 及 Queue 介面 , 因
此也具備 Queue 介面的 offer() 、 TIIP
poll() 、 peek() 等方法 , 這些稍後即會介紹。
18-1-6 Queue 介面與相關類別
 Queue 介面主要是實作『先進先出』 (FIFO, First
In Fisrt Out) 的集合存取方式 , 提供了以下 6 種方
法來存取元素 , 前 3 種是會丟出例外的版本 , 而後
3 種則不會:
Queue 介面與相關類別
 實作 Queue 介面的類別 , 除了前述的 LinkList 類
別 ( 同時實作 List 及 Queue 介面 ) 外 , 常用的還
有 PriorityQueue :
PriorityQueue
 PriorityQueue 會依照特定的優先順序 (Priority) 來
排序 , 高優先的元素會排在較前面 , 因此會優先被取
出 (FIFO) 。
 優先順序可以是自然順序 ( 依字母或數值由小到大
排 ), 也可以自訂順序 ( 例如使用 Comparotor 來排
序 ), 有關排序的說明請參閱第 18-1-10 節。底下是
依整數排序的佇列 ( 越小越優先 ) 。
PriorityQueue
18-1-7 Map 介面與相關類別
 Map 介面是用來存放『鍵值對』 (key-value pair)
對應關係的元素集合 , 在加入元素時 , 必須指定此元
素的 key 及 value 兩項內容 , 而且 key 的內容不
可重複。
 例如有個學生的集合 , 就可用學號為『鍵』、學生姓
名為『值』。
 如果在加入元素過程中 , 加入了重複的『鍵』 , 則新
的值會取代舊的值。
Map 介面與相關類別
 由於上述的特性 , 因此 Map 介面只有 isEmpty() 、
size() 這兩個方法和 Collection 的同名方法相似 , 而
元素的新增 / 移除需改用下列方法:
Map 介面與相關類別
 至於整個 Map 集合的操作則有以下的方法可使用:
Map 介面與相關類別
 至於 SortedMap 介面除了繼承 Map 介面外 , 還提
供以下幾個類似於 SortedSet 的方法:
Map 介面與相關類別
 Map/SortedMap 的相關類別關係如下:
Map 介面與相關類別
 AbstractMap 和 AbstractCollection 一樣定義了
toString() 方法 , 所以也可呼叫 System.out.println()
方法直接輸出 Map 類型物件的內容。
 雖然 AbstractMap 有 5 種衍生類別 , 但一般只用
到 HashMap 、 TreeMap 和 LinkedHashMap ,
HashMap 就像 Set 中的 HashSet, 是用來存放不
需強調先後次序的鍵值對集合。
Map 介面與相關類別
 至於對應的 TreeMap 也和 TreeSet 一樣 , 會自動
將集合中的鍵值對排序 ( 預設是以『鍵』物件的排序
為準 ) , 所以適合用來存放需排序的鍵值對 , 除了用
鍵搜尋值的執行效率較佳另外 , 也提供
headMap() 、 subMap() 、 tailMap() 來取出子集合
( 後備集合 ) 。
 至於 LinkedHashMap 則和 LinkedHashSet 類似 ,
會維持鍵值對加入時的順序。
 以下就是一個簡單的 HashMap 範例。
Map 介面與相關類別
Map 介面與相關類別
Map 介面與相關類別

1. 第 12 行建立一個新的 HashMap 物件。
2. 第 14 、 15 行的 for 迴圈逐一將 cities 陣列的內
容以鍵值對的方式加入 HashMap 物件中。
3. 第 20 、 21 行加入新的鍵值對並輸出內容。
4. 第 24 行用重複的鍵加入鍵值對 , 結果會取代掉原
有的鍵值對。
古老的 Vector 及 Hashtable 類別
 ArrayList 和 HashMap 各有一個歷史悠久的相同類別:

 這二個古老類別的特別之處 , 就是為『 多緒安全』
(Thread safe) 的 , 因為其主要的方法都已設定同步化
(Synchronized) 。
 其中 Hashtable 的 t 沒有大寫 ( 由此可見其歷史的古
老 ), 並且和 HashMap 還有一個差別 , 就是其 key 與
value 都不允許為 null, 而 HashMap 則允許有一個 null
的 key, 以及不限數目的 null value 。
▪ 這二個古老類別也在 SCJP 的考試範圍 , 所以請記住
新、舊類別的對應關係及差別。
18-1-8 Iterator 迭代器
 Iterator ( 迭代器 ) 是 java.util 套件中的另一個介面 ,
它並不是用來建立集合 , 而是用來逐一瀏覽集合中所
有元素的一項工具。
 所有 Collection 類別都有個
 iterator() 方法 , 以集合物件呼叫此方法可傳回一
Iterator 物件 , 我們可用此物件呼叫 Iterator 介面的
方法來逐一取得、移除集合中的元素。
 Map 型的類別則無 iterator() 方法 , 但我們可用
18-26 頁所列的 entrySet() 等方法取得代表 Map
物件的集合物件 , 再用它建立 Iterator 物件。
Iterator 迭代器
 Iterator 介面提供了 3 個瀏覽集合物件的方法:

 以下我們稍稍修改前面的 SubTree.java 範例程式 ,
示範 Iterator 介面的用法。
Iterator 迭代器
Iterator 迭代器
Iterator 迭代器
1. 第 8 〜 10 行建立一個 TreeSet 物件並將 1 到
100 的整數存入其中。
2. 第 14 行建立 Iterator 物件 i 。
3. 第 16 〜 18 行用 while() 迴圈逐一讀取所有元素 ,
並檢查該元素值是否可被 9 整除 , 不行就將元素移
除。
4. 第 20 行輸出最後的集合內容 , 就只剩下 9 的倍
數。
For-Each 迴圈
 在介紹陣列時 , 曾簡單介紹過 For-Each 迴圈這個
JDK 5.0 新增語法的用法 , 其實 For-Each 迴圈也
非常適合用於集合物件上。
 For-Each 迴圈用於集合時 , 要指定一個您要『逐一
讀取所有元素』的集合物件 , 以及一個代表單一元素
的變數 , 其它部份則和 for 迴圈相同。 For-Each 迴
圈的語法如下:
For-Each 迴圈
 底下直接修改前一個範例程式 , 來示範 For-Each 迴
圈的用法:
For-Each 迴圈
For-Each 迴圈
1. 第 8 行使用本章開頭介紹的 JDK 5.0 新語法 , 以
<Integer> 標示 TreeSet 集合是用來存放整數物件
。
2. 第 15 即為 For-Each 迴圈 , 括號中前半宣告一個
Integer 物件 i, 後半則是集合物件 IntTS 。所以
For-Each 迴圈的作用就是:『從頭開始 , 依序取出
IntTS 中的每個元素』 , 而每一輪迴圈中可用物件 i
來代表該輪迴圈正在使用的元素。
18-1-9 集合的排序
 前面介紹的 TreeSet 、 TreeMap 、及
PriorityQueue 都是屬於會自動排序的集合類別 , 它
們通常會使用自然順序 (Natural order) 來排序 , 例
如 String 就是依照字母的順序 (A 小於 B, 若第一
個字母相同就比第二個 ), 而 int 則是依照數值大小 ,
由小到大排序。
 但如果集合中的元素是我們自行定義的類別 , 那麼這
個自然順序就必須由我們自行定義 , 否則會編譯錯誤
, 因為 Java 也不知道要如何排序。
集合的排序
 設定類別的排序方法有二種 , 第一種是讓類別實作
Comparable 介面的 compareTo() 方法 , 此方法其
實就是在定義『比較大小』的方法 , 也就是定義類別
的自然順序。
 另一種方法則不須更改原來的類別 , 而是另外定義一
個實作 Comparator 介面的類別來幫忙排序。
Comparable 介面:讓類別具備排序能力
 只要讓類別實作 Comparable 介面 , 並重新定義介
面的 CompareTo() 方法 , 即可讓該類別具備排序能
力。底下來看範例:
Comparable 介面:讓類別具備排序能力
Comparable 介面:讓類別具備排序能力
 第 10~11 行就是在重新定義 Comparable 介面的
compareTo() 方法 , 注意必須宣告為 public int, 而
傳回值若為 0 表示相等 , 大於 0 表示物件較大
( 大於參數傳入的物件 ), 小於 0 則物件較小。
▪ Comparable 也有泛型的版本 , 例如將以上的介面改
為 Comparable<Student>, 則 compareTo(Objet o)
就可改為 compareTo(Student o), 那麼在方法中就不
需再用 (Student) 來將 o 轉型了。 ( 有關泛型請參閱
18-2 節 )
Comparable 介面:讓類別具備排序能力
2. 在 compareTo() 中要比較的資料若是
String 、 Integer 等基本型別 , 那麼也可使用其內
建的 compareTo() 方法來比較 , 例如第 11 行也
可改為:

3. 第 17~20 行分別加入 3 個學生然後印出排序結果
。
Comparable 介面:讓類別具備排序能力
 請注意 , 對於任何要排序的集合來說 , 其內部元素的
型別最好都相同 , 否則在呼叫元素的 CompareTo()
比較大小時 , 可能會發生無法轉型的錯誤。例如:

 以上在 TreeSet 集合中先存入 5, 然後再存入 6L
時丟出了 ClassCastException 例外。
 這是因為存入 6L 時 , 會自動呼叫 6L (Long) 的
compareTo(Long 其他元素 ) 來排序 , 但其他元素
(5, 為 Integer) 則因無法轉型為 Long 而發生錯誤
。
Comparator 介面:定義幫 忙排序的類別
 實作 Comparable 介面時必須更改原始的類別 , 如
果不想 ( 或不能 ) 更改原始類別 , 或是想要建立多種
排序方式以供不同的狀況使用 , 那麼就可改用
Comparator 介面 , 另外定義新的類別來幫忙排序。
 底下程式定義了二種可幫忙 Student 排序的新類別
:
Comparator 介面:定義幫 忙排序的類別
Comparator 介面:定義幫 忙排序的類別

1. 第 13 及 17 行分別定義了二個幫忙排序的新類別 ,
並實作了 Comparator 介面的 compare() 方法 ,
此方法須傳入 2 個要比較的物件 , 而傳回值則與
Comparable 介面的 compareTo() 一樣。
▪ Comparator 也有泛型的版本 , 例如將以上的介面改
為 Comparator<Student>, 則 compare() 中的參數
就可改為 compareTo(Student o1, Student o2), 那麼
在方法中就不需再用 (Student) 來將 o1 、 o2 轉型
了。 ( 有關泛型請參閱 18-2 節 )
Comparator 介面:定義幫 忙排序的類別
2. 第 23 及 28 行分別利用新建立的排序物件來建構
TreeSet 物件 , 因此會都以排序物件中所定義的
comare() 方法來排序。
有了這種功能 , 我們就可以預先定義多種不同的排
序類別 , 然後視需要而選用不同的排序方式。下面
是各自動排序集合的相關建構方法:
18-1-10 Collections 和 Arrays 類別
 Collections 類別與 Arrays 類別中包含了許多實用
的靜態方法 , 可分別針對集合與陣列進行排序、搜尋
、顛倒順序 ... 等操作。
 其中的 Collections 類別請不要和 Collection 介面
弄混了 , 二者並沒有直接的關係。
Collections 和 Arrays 類別
 Collections 的排序與搜尋功能 , 主要是針對 List 所
設計 , 例如 ArrayList 類別。
 而 Arrays 類別則主要是針對陣列進行操作 , 下面列
出這二個類別的常用功能:
Collections 和 Arrays 類別
Collections 和 Arrays 類別
 底下先建立 Book 類別及其排序類別 (Book.java),
然後再示範排序功能:
Collections 和 Arrays 類別
Collections 和 Arrays 類別
 以上在 Book 類別中定義了繼承自 Object 的
toString() 、 equals() 、及 hashCode() 方法 , 也定
義了比較大小的 compareTo() 方法來實作其自然順
序。
 BookByName 類別則是可依書名遞增排序的類別。
接著就使用這些類別來撰寫排序程式。
Collections 和 Arrays 類別
Collections 和 Arrays 類別
Collections 和 Arrays 類別

1. 第 4~9 行是陣列的建立及排序 , 第 11 行將陣列
轉為 List 來建構 ArrayList 物件 , 然後進行 2 種
排序。
2. 第 17~21 行示範 List 的反轉排序 , 第 23~27 行
則示範 List 的二分搜尋。
Collections 和 Arrays 類別
 使用 Collections.binarySearch() 來搜尋時 , 必須注
意以下幾點:
▪ 被搜尋的陣列或 List 必須已排序過。若是依自然順序
排 , 則可直接搜尋;但如果是使用自訂的 comparator
排序 , 則在 binarySearch() 的參數中也必須加上相同
的 comparator 物件。
▪ 如果搜尋到了 , 會傳回元素的位置 ( 大於等於 0 的
int) 。
▪ 如果沒找到 , 則傳回一個負數 , 代表插入點的位置
( 就是搜尋的 key 若插入 List 中 , 所應插入的位
置 ) 。當傳回 x 時 , 則插入點位置為 (-x-1), 例如上
例中搜尋 103 時傳回的 -2, 就表示其插入點應為 (-(2)-1) = 1 。
18-1-11 TreeSet 和 TreeMap 的導航
 自 Java 6 開始 , 針對 TreeSet 和 TreeMap 分別
新增了導航 (Navigating) 的 NavigatableSet 及
NavigatableMap 介面:
Collections 和 Arrays 類別
 在 NavigatableSet 介面中增加了以下方法:
Collections 和 Arrays 類別
 在 NavigatableMap 介面中也增加類似的方法 , 但
在方法的名稱中多了 Key 或 Entry :

 底下程式分別示範 Java 5 及 Java 6 的寫法 , 以展
示新導覽介面的效用。
Collections 和 Arrays 類別
Collections 和 Arrays 類別
1. 第 8 、 9 行是 Java 5 的寫法 , 必須先用
headSet() 取出小於 140 元的子集合 , 然後再用
last() 取出子集合的最後一個元素。
2. 第 12 行是 Java 6 的寫法 , 直接用 lower() 來讀
取即可。
Collections 和 Arrays 類別
 另外 , NavigatableXxx 介面 (Xxx 表示 Set 或
Map) 也多重定義了
headXxx() 、 tailXxx() 、 subXxx() 方法 , 在這方法
中多加了一個 boolean 參數 , 代表傳回的子集合中
是否要『排除』做為分界的元素 , 例如:

▪ 有加 boolean 參數的方法會傳回 NavigatableXxx 型
別的集合 , 沒加 boolean 參數時則傳回 SortedXxx
型別的集合。
18-2 泛型 (Generic Types)
 當我們在宣告陣列時都一定會指定型別 , 因此陣列是
型別安全 (Type Safe) 的 , 因為只有符合型別的資
料才能存入陣列中。
 也就是說 , 我們無法將 String 物件存入 Integer 的
陣列中 , 因為型別不符。
 然而 , 當我們在存取集合時 , Java 就無法幫我們檢
查型別了 , 因為集合預設是可以接受任何物件 ( 即
Object 及其衍生類別 ) 的。
18-2-1 使用泛型集合
 為了讓集合 ( 以及其他我們自訂的類別 ) 也可以是型
別安全的 , 自 Java 5 開始加入了泛型 (Generic
Types) 的功能 , 我們只要在集合類別名稱之後加上
"< 型別 >", 就可讓該集合只能存入指定型別的資料 ,
例如:
使用泛型集合
 像以上緊接在類別之後的 < 型別 > 就是泛型型別 ,
也稱為型別參數 (Type parameter) 或參數化型別
(Parameterized type), 因為它就像是類別的參數一樣
。
 一旦宣告了集合的泛型型別 , 那麼在讀取集合元素時
, 也不需要再做轉型的動作了 , 例如:
使用泛型集合
 事實上 , 編輯器知道 tm.get(1) 會傳回 String 型別 ,
因此不但不用轉型 , 而且還會幫我們檢查型別。
 所以如果使用 int 變數來接收傳回值 , 就會發生編譯
錯誤。
在參數及傳回值 使用泛型集合
 在使用泛型集合時 , 除了宣告及建構物件時要加型別
參數外 , 自訂方法的參數或傳回值也可加上型別參數
。
 例如底下的 newBook( ) 方法可接收及傳回
ArrayList<Book> 集合 (Book 類別定義在
Book.java, 參見 8-1-10 節 ) 。
在參數及傳回值 使用泛型集合
在參數及傳回值 使用泛型集合
18-2-2 混合使用泛型與非泛型的集合
 為了與舊版的程式相容 , Java 允許我們混合使用泛
型與非泛型的集合 , 例如以下程式是合法的:
混合使用泛型與非泛型的集合
混合使用泛型與非泛型的集合
1. 第 5 、 6 行將泛型集合與非泛型的參照相互指定 ,
並在第 10 行將 newBook 方法的參數及傳回值都
改為非泛型集合 , 然後以泛型集合 b 為參數來呼叫
, 這些寫法都是合法的。
2. 在第 14 行讀取非泛型集合的元素時 , 記得必須先
強制轉型為 Book 後才能使用。
混合使用泛型與非泛型的集合
3. 凡是使用了『非泛型』集合的程式 , 在編譯時都會
出現二行 Note 警告 , 第一行是提示您使用了未檢
查或不安全 ( 型別 ) 的操作 , 第二行則建議您使用
-Xlint:unchecked 參數重新編譯 , 以便顯示較詳細
的相關說明。
不過這些都只是警告而已 , 程式仍可正常編譯及執
行。
18-2-3 泛型的『型別消除』特性與注意事項
 由於 Java 只有在『編譯時』才會檢查及處理泛型的
型別 , 然後會經過一個型別消除 (Type erasure) 的
過程 , 將型別參數由程式碼中消除。
 因此在 .class 的二元碼 (Byte code) 中完全沒有泛
型的資訊 , 而在執行時自然也就無法幫我們檢查型別
參數的正確性了。
泛型的『型別消除』特性與注意事項
 因此 , 混用泛型與非泛型的集合時 , 有可能會發生一
些危險的狀況 , 例如修改以上的 newBook() 方法:
泛型的『型別消除』特性與注意事項
 以上程式宣告一個 TreeSet<Book> 的集合物件 , 但
呼叫 newBook() 時卻在其中加入一個 String 元素 ,
此時由於方法的參數為 TreeSet ( 非泛型的集合 ),
所以編譯器不會發現問題。
 而在編譯成功並執行到第 6 行時 , 則會因取出來的
元素無法轉換為 Book 型別而丟出例外。
泛型的『型別消除』特性與注意事項
 也就是說 , 程式執行到第 10 行將錯誤的 String 物
件加入集合時 , JVM 並不會發現錯誤而丟出例外!
 要一直等到稍後讀取該錯誤元素時 , 才會因型別轉換
錯誤而丟出例外。
 但如果程式較為複雜 , 或是很久以後才讀到該錯誤元
素 , 那麼我們將很難找出錯誤發生的地點。因此最好
盡量少用非泛性的集合。
▪ 陣列和泛型集合不同 , 陣列在編譯和執行時都會檢查
型別 ( 而不會型別消除 ), 因此即使將 Integer 陣列當
成 Object 陣列來操作 , 在執行時也不允許將 String
資料加入陣列 ( 會丟出 ArrayStoreException 的例
18-2-4 泛型與多型
 我們知道 Java 具備『多型』的特性 , 也就是可以將
子類別的物件指定給父類別 , 例如:

 那麼泛型是否也具備多型的特性呢?
 其實泛型的整個型別可分為二部份 , 以
TreeSet<Long> 為例 , 前面的 TreeSet 稱為基底型
別 , 而 <> 中的 Long 則為泛型型別。
泛型與多型
 基底型別具備多型的特性 , 而泛型型別則不具備 , 也
就是說 , 泛型型別必須完全一樣才行。例如:
泛型與多型
 對集合來說 , 泛型的型別其實就是集合中元素的型別
, 因此需要嚴格的限制 , 否則一旦讓元素為 Integer
的集合變成元素為 Object 的集合 , 那麼編譯器將無
法阻止任何的非法存入了。
 不過請不要弄混了 , 當我們對集合中的元素操作時 ,
則是允許多型的。
泛型與多型
泛型與多型
▪ 如果是自動排序的集合 , 則所有元素都必須可相互比
較大小才行。例如若將上面的 ArrayList 換成
TreeSet, 則執行到第 6 行時會丟出
ClassCastException 例外 , 因為無法將 5 (Integer)
轉為 Long 來呼叫 6L 的 compareTo(Long) 方法。
18-2-5 泛型的通用型別
 雖說泛型型別不具備多型的特性 , 但在某些情況下確
有多型的必要性 , 例如我們要寫一個反向排序的方法
, 其操作對象可以是
TreeSet<Integer> 、 TreeSet<Long>... 等元素為基
本型別的 TreeSet 集合 , 那麼就可使用通用型別
(Wildcard type) : <?> 來定義:
泛型的通用型別
泛型的通用型別
1. 第 2 行以通用泛型型別 List<?> 來定義 sortList()
的參數及傳回值。
2. 第 7 、 11 、 15 行則分別建立不同泛型型別的
ArrayList 集合 , 然後存入資料、進行排序、及列印
內容。
限制通用型別的範圍
 以上的 sortList() 如果希望只能接受泛型型別為
Number 或其衍生類別的參數 , 則可改寫為:
 那麼前面第 17 行就會編譯錯誤了 , 因為 String 不
是 Number 的衍生類別。
 請注意 extends 後面可以是一個類別 , 也可以是一
個介面 , 但千萬不可以將 extends 改為
implements 。
限制通用型別的範圍
 另外要注意的是 , List<Object> 和 List<?> 是不同的
。
 前者只允許參照或傳入 List<Object> 型別的集合 ,
而後者則可以是 List< 任何型別 >, 例如
List<String> 或 List<Book> 都可以。
通用型別的限制與 super
 使用通用型別有一個限制 , 就是不允許在通用型別的
集合中存入資料!
 這當然是為了要維護型別安全 , 例如在上面的
sortList() 中增加一行:
限制通用型別的範圍
 那麼在編譯時 , 第 3 行會發生 cannot find symbol
的錯誤 , 表示 aList 不能用 add() 來加入資料。
 此時如果編譯器不阻止的話 , 萬一傳入的是
ArrayList<Long> 集合 , 那麼在方法中加入 Integer
資料又會發生難以查覺的錯誤了。
 不過 , 如果真的需要在方法中加入某型別的元素 , 那
麼可將參數改為 <?super 某型別 > 的寫法 , 表示
只能傳入某型別或其上層型別 , 例如。
限制通用型別的範圍
限制通用型別的範圍
 如果傳入的集合其泛型型別都是 Integer 或其上層型
別 ( 例如 Number 或 Object), 那麼在方法中用
add() 加入 Integer 資料就不會有問題 , 所以可以通
過編譯器的檢查。
 但如果傳入了 ArrayList<String> 或
ArrayList<Book> 集合 , 則會因不符 super 的限制
而編譯錯誤;同理 , 若在方法中加入非 Integer 的資
料 , 那麼也會編譯錯誤。
▪ 以上程式雖說不會有問題 , 但若傳入方法的是
ArrayList<Number> 集合 , 則在排序時要小心 , 因為
其內若存放著 Integer 、 Long 等不同型別的元素 ,
那麼在排序時就會發生錯誤 ( 丟出例外 ) 。
限制通用型別的範圍
 最後要注意的是通用型別只能用在宣告上 ( 變數、參
數、及傳回值 ), 而不可用來建構物件 , 因為物件一
定會有固定的型別。底下是一些範例:
18-2-6 建立泛型類別
 在學會使用泛型集合之後 , 其實我們也可以定義自己
的泛型類別。首先來查看一下集合類別的 API 文件 ,
底下以 TreeSet 為例:
建立泛型類別

 其中的 E 就代表泛型的型別 , 當我們宣告
TreeSet<String> 物件時 , 其相對的父類別就是
AbstractSet<String>, 而其建構方法及一般方法中的
E 也都會換成 String 。例如要加入元素時 , 就必須
呼叫 add(String e) 的方法。
建立泛型類別
 以上的 E 其實只是一個代表泛型型別的名稱 , 您也
可以換成其他合法的識別字 , 例如
T 、 K 、 GenType.. . 等 , 只要不和其他識別字重複
即可。
 在集合中的泛型名稱通常會使用 E, 代表其為
Element( 元素 ) 的型別;在其他地方則習慣以 T 表
示 , 就是 Type 的意思。
定義泛型類別
 若要定義自己的泛型類別 , 只要在類別名稱後面加上
<T>, 然後在類別中以 T 來代表泛型型別即可。例如
底下定義一個簡易版的商店類別:
定義泛型類別
 其中的 T 就代表商品的型別 , 例如要開書店 , 就可
用前面設計過的 Book 類別。
 一旦在類別名稱之後加上了 <T>, 那麼 T 這個型別
就可在類別中任意使用了 , 等到將來實作物件時 , 類
別中所有的 T 都會以指定的泛型型別 ( 例如 Book)
取代。
▪ 泛型型別也可以有多個 , 例如 class StoreEz<T,U>,
則會有 T 、 U 二個泛型型別 , 均可出現在類別的定
義中。當然 , 在使用此類別時也須指定二個型別 , 例
如 StoreEz<Book,String> 。
定義泛型類別
 以上類別宣告了一個 List<T> 來參照商品列表 , 在
建構方法中則須傳入該商品列表。
 get(index) 會傳回列表中 index 位置的 T 型別商品
, sale(T) 會將指定商品自列表中移除 , 而 report()
則可列出商品清單。底下就是書店程式:
定義泛型類別
定義泛型類別
1. 第 5~8 行建立商品清單並存入 3 本書。然後在第
10 行用此清單來建構商店物件 , 接著印出店內的商
品清單。 Book 類別的程式碼請參見第 18-1-10 節
。
2. 第 13~16 行取出第 2 本書 ( 索引為 1), 然後賣
掉 , 最後印出店內的商品存貨。
限制泛型型別的範圍
 如果想讓前面的商店類別也能計算金額 , 可先讓每種
商品類別都提供 getPrice() 方法來傳回訂價 , 如此
一來 , 在商店類別中就可呼叫商品的 getPrice() 來
計算價錢了。
 然而 , 如果在商店類別中直接呼叫 getPrice(), 此時
由於商品的型別 (T) 未確定 , 因此編譯器會視為錯
誤 , 因為無法保證未來的商品一定有此方法。
 為了保證未來的商品都有 getPrice() 方法 , 最好的
做法 , 就是定義一個具備此方法的介面 , 然後限定只
有此介面的衍生類別才能做為泛型型別。
限制泛型型別的範圍
 以下就是新的 Salable 介面及 Store 商店類別:
限制泛型型別的範圍
限制泛型型別的範圍
 第 3 行就是可供商品類別實作的 Salable 介面 , 其
內只宣告了 getPrice() 方法。
 在第 6 行定義泛型類別時 , 則限定所有的 T 都必
須衍生自 Salable 介面 , 如此一來 , 在第 19 行呼
叫商品的 getPrice() 方法時 , 編譯器就會開心地讓
我們過關了。
 另外 , 在類別中也多加了一個 balance 變數來代表
店內現金 , 並在相關方法中加入處理現金的程式。
 底下我們就另外開一家 T 恤商店吧。
限制泛型型別的範圍
限制泛型型別的範圍
限制泛型型別的範圍

1. 第 2 行是定義商品類別: Tshirt, 此時必須實作
Salable 介面 , 否則無法放在 Store 中販賣 ( 因為
Stroe 的泛型型別只接受 Salable 的衍生類別 ) 。
2. 第 9 行實作了 Salable 介面的 getPrice() 方法。
其他的程式都和前面的簡易版商店差不多 , 就不再
多做解釋了。
18-2-7 建立泛型方法
 有時我們只想將泛型使用在某個方法之中 , 而不需要
套用在整個類別 , 這時候就可以定義泛型方法。
 做法很簡單 , 只要將 <T> 放在方法的傳回型別之前
, 那麼就可將 T 用在方法的參數、內容、及傳回型
別之中了。
 例如底下是可產生 ArrayList 並存入二個元素的泛型
方法:
建立泛型方法
建立泛型方法
1. 第 3 行就是在定義泛型方法 , 請注意 <T> 必須放
在傳回型別 TreeSet<T> 之前。加上 <T> 之後 , T
就可任意出現在方法的參數、內容、及傳回型別之
內了。
2. 第 10 行呼叫泛型方法 , 這裡要注意的是 , 『傳入
參數的型別』就會變成泛型型別 , 也就是會取代 T
的型別。此處傳入 String 物件 , 因此會建立
TreeSet<String> 的集合。
建立泛型方法
3. 第 12 行則以 Book 物件呼叫泛型方法 , 因此會建
立 TreeSet<Book> 的集合。有關 Book 類別的程
式碼請參見第 18-1-10 節。
▪ 在宣告泛型類別的變數、參數、或傳回值時 , 可以用
<?> 來表示通用泛型型別;但在定義泛型類別或泛型
方法時 , 則不可使用 <?>, 而必須使用如
<E> 、 <T> 等識別字來代表型別參數。
▪ 類別名稱、型別參數名稱、與變數名稱分屬不同的命
名空間 , 因此名稱重複時並不會造成衝突。例如
class T { <T> void T(T T) { } ; } 是合法的 , 雖然有點
荒謬。 ( 這 5 個 T 分別是:類別名稱、泛型型別、
建構方法名稱、泛型型別、參數名稱。 )
 18-A 利用集合物件產生樂透號碼
 18-B 陽春型英漢字典
1. Given:

What is the result?
A. b,a,c,

B. c,a,b,

C. c,b,a,

D. Compilation fails. E. An exception is thrown at runtime.
2. Please place the correct Strings to the Output empty boxes.
3. Which class implements the NavigatableMap ?
A. HashMap.
B. SortedMap.
C. TreeMap.
D. LinkedHashMap. E. NavigateMap.
4. Given:
What is the result?
A. 2
B. 3
C. 4
D. 16
E. Compilation fails.
5. Given:

What is the result?
A. 3.14 50 100
B. 100 50 3.14
C. Compilation fails.
D. An exception is thrown at line 4.
E. An exception is thrown at line 5.
F. An exception is thrown at line 7.


Place the correct Animals to the Outputs empty boxes.
(Animals can be used more than once . )
7. Which two, inserted at line 1, will compile? (Choos two.)

A.
B.
C.
D.
E.
F.

class MaxValue<X> {
class MaxValue<X extends Object> {
class MaxValue<X extends Number> {
class MaxValue<X extends Integer> {
class MaxValue<?> {
class MaxValue<? extends Number> {
8. The following class is designed for the key value of
java.util.HashMap.

Which two method should be overridden to make the
MyKey work correctly? (Choose two.)
A. public String toString()
B. public boolean equals(MyKey k)
C. public int compareTo(MyKey k)
D. public int hashCode()
E. public boolean equals(Object k)
9. Given:

Which code, inser ted a t line 4, will guarantee to output [4, 5]?
A. Set x = new HashSet();
B. Set x = new SortedSet();
C. Set x = new TreeSet();
D. List x = new ArrayList();
E. List x = new LinkedList();

Mais conteúdo relacionado

Semelhante a SCJP ch18

Java程序员面试之葵花宝典
Java程序员面试之葵花宝典Java程序员面试之葵花宝典
Java程序员面试之葵花宝典yiditushe
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001rainx1982
 
各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索
各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索
各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索Chui-Wen Chiu
 
Java面试知识
Java面试知识Java面试知识
Java面试知识yiditushe
 
Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题yiditushe
 
Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制yiditushe
 
Flash基于对象的优化技术 黄炎中
Flash基于对象的优化技术 黄炎中Flash基于对象的优化技术 黄炎中
Flash基于对象的优化技术 黄炎中FLASH开发者交流会
 
Effective java 摘選條目分享 1 - 物件、複合、可變性、leak
Effective java   摘選條目分享 1 - 物件、複合、可變性、leakEffective java   摘選條目分享 1 - 物件、複合、可變性、leak
Effective java 摘選條目分享 1 - 物件、複合、可變性、leakKane Shih
 
招聘笔试题(二)
招聘笔试题(二)招聘笔试题(二)
招聘笔试题(二)yiditushe
 
第01章 绪论(java版)
第01章  绪论(java版)第01章  绪论(java版)
第01章 绪论(java版)Yan Li
 
Java面试笔试题大汇总
Java面试笔试题大汇总Java面试笔试题大汇总
Java面试笔试题大汇总yiditushe
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程yiditushe
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程appollo0312
 

Semelhante a SCJP ch18 (20)

SCJP ch17
SCJP ch17SCJP ch17
SCJP ch17
 
SCJP ch08
SCJP ch08SCJP ch08
SCJP ch08
 
Java程序员面试之葵花宝典
Java程序员面试之葵花宝典Java程序员面试之葵花宝典
Java程序员面试之葵花宝典
 
SCJP ch12
SCJP ch12SCJP ch12
SCJP ch12
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
 
各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索
各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索
各種酷炫圖表繪製技術 Silverlight Toolkit 與 MS Chart 控制項大探索
 
C語言結構與串列
C語言結構與串列 C語言結構與串列
C語言結構與串列
 
Java面试知识
Java面试知识Java面试知识
Java面试知识
 
Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题
 
Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制
 
Flash基于对象的优化技术 黄炎中
Flash基于对象的优化技术 黄炎中Flash基于对象的优化技术 黄炎中
Flash基于对象的优化技术 黄炎中
 
Effective java 摘選條目分享 1 - 物件、複合、可變性、leak
Effective java   摘選條目分享 1 - 物件、複合、可變性、leakEffective java   摘選條目分享 1 - 物件、複合、可變性、leak
Effective java 摘選條目分享 1 - 物件、複合、可變性、leak
 
Sun java
Sun javaSun java
Sun java
 
招聘笔试题(二)
招聘笔试题(二)招聘笔试题(二)
招聘笔试题(二)
 
第01章 绪论(java版)
第01章  绪论(java版)第01章  绪论(java版)
第01章 绪论(java版)
 
Java面试笔试题大汇总
Java面试笔试题大汇总Java面试笔试题大汇总
Java面试笔试题大汇总
 
Js培训
Js培训Js培训
Js培训
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
 
Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程Struts+Spring+Hibernate整合教程
Struts+Spring+Hibernate整合教程
 
getPDF.aspx
getPDF.aspxgetPDF.aspx
getPDF.aspx
 

Mais de r82093403

Ciw going mobile
Ciw going mobileCiw going mobile
Ciw going mobiler82093403
 
The lpic 2 exam prep
The lpic 2 exam prepThe lpic 2 exam prep
The lpic 2 exam prepr82093403
 
Exploration network chapter3
Exploration network chapter3Exploration network chapter3
Exploration network chapter3r82093403
 
Exploration network chapter7
Exploration network chapter7Exploration network chapter7
Exploration network chapter7r82093403
 
Exploration network chapter11
Exploration network chapter11Exploration network chapter11
Exploration network chapter11r82093403
 
Exploration network chapter10
Exploration network chapter10Exploration network chapter10
Exploration network chapter10r82093403
 
Exploration network chapter9
Exploration network chapter9Exploration network chapter9
Exploration network chapter9r82093403
 

Mais de r82093403 (20)

Ciw going mobile
Ciw going mobileCiw going mobile
Ciw going mobile
 
The lpic 2 exam prep
The lpic 2 exam prepThe lpic 2 exam prep
The lpic 2 exam prep
 
SCJP ch16
SCJP ch16SCJP ch16
SCJP ch16
 
SCJP ch15
SCJP ch15SCJP ch15
SCJP ch15
 
SCJP ch14
SCJP ch14SCJP ch14
SCJP ch14
 
SCJP ch11
SCJP ch11SCJP ch11
SCJP ch11
 
SCJP ch10
SCJP ch10SCJP ch10
SCJP ch10
 
SCJP ch09
SCJP ch09SCJP ch09
SCJP ch09
 
SCJP ch06
SCJP ch06SCJP ch06
SCJP ch06
 
SCJP ch05
SCJP ch05SCJP ch05
SCJP ch05
 
SCJP ch04
SCJP ch04SCJP ch04
SCJP ch04
 
SCJP ch03
SCJP ch03SCJP ch03
SCJP ch03
 
SCJP ch02
SCJP ch02SCJP ch02
SCJP ch02
 
SCJP ch01
SCJP ch01SCJP ch01
SCJP ch01
 
SCJP ch13
SCJP ch13SCJP ch13
SCJP ch13
 
Exploration network chapter3
Exploration network chapter3Exploration network chapter3
Exploration network chapter3
 
Exploration network chapter7
Exploration network chapter7Exploration network chapter7
Exploration network chapter7
 
Exploration network chapter11
Exploration network chapter11Exploration network chapter11
Exploration network chapter11
 
Exploration network chapter10
Exploration network chapter10Exploration network chapter10
Exploration network chapter10
 
Exploration network chapter9
Exploration network chapter9Exploration network chapter9
Exploration network chapter9
 

SCJP ch18

  • 1. 第 18 章 集合與泛型 本投影片(下稱教用資源)僅授權給採用教用資源相關之旗標書籍為教科書之授課老師(下稱老師)專用,老 師為教學使用之目的,得摘錄、編輯、重製教用資源(但使用量不得超過各該教用資源內容之 80% )以製作為 輔助教學之教學投影片,並於授課時搭配旗標書籍公開播放,但不得為網際網路公開傳輸之遠距教學、網路教 學等之使用;除此之外,老師不得再授權予任何第三人使用,並不得將依此授權所製作之教學投影片之相關著 作物移作他用。 著作權所有 © 旗標出版股份有限公司
  • 2. 學習目標  學習使用 Collection 相關類別與介面  活用集合的排序功能  管理集合與陣列的 Collections 和 Arrays 類別  瞭解泛型的意義  活用泛型集合  學習泛型的通用型別  自訂泛型類別與泛型方法
  • 3. 前言  在 java.util 套件中 , 還有一個相當重要的集合 (Collection) 類別群 , 其中包含了可以儲存、處理各 種資料結構的類別 , 例如 ArrayList 、 LinkList 、 TreeSet 等 , 對於在程式中 處理資料時很有幫助。  而當我們在集合中進行搜尋或排序時 , 還會使用到物 件繼承自 Object 類別的 equals() 及 hashcode() 方法 , 以上這些在本章中都會詳細介紹。
  • 4. 前言  此外 , 自 Java 5 才有的泛型 (Generic Type) 功 能 , 主要是用來提升型別檢查的安全性。  除了可使用在現有的集合類別之外 ( 例如宣告一個 只能儲存 String 物件的集合 ), 我們也可用它來定 義自己的泛型類別或泛型函式 , 讓同樣的程式碼能 安全地處理各種不同型別的資料。
  • 5. 18-1 集合 (Collection)  在撰寫程式時 , 常需要處理一群同性質資料的集合 , 為了方便程式處理這樣的集合 , Java API 特別在 java.util 套件中提供了一組介面和類別 , 讓我們可建 立這類資料集合的物件 , 這個物件就稱之為 Collection ( 集合物件 ) 。  為了使這些集合類別有一致性 , Java 特別將 Collection 相關介面設計成一完整的 Collections Framework 架構。
  • 6. 集合 (Collection)  以下我們先簡介 Java 的 Collections Framework, 然後再分類說明各集合類別的用法。 ▪ Collections Framework 為 SCJP 考試重點之一 , 請 務必熟悉這個主題。
  • 7. 18-1-1 Collections Framework 簡介  當程式需要處理集合式的資料時 ( 例如要處理大量的 學生資料 ) , 就可利用 Collections Framework 中的 集合類別來建立代表學生資料的集合物件 , 接著即可 利用集合類別提供的現成方法 , 來處理集合物件中的 每一筆資料。
  • 8. Collections Framework 簡介  Collections Framework 的核心 , 就是各集合相關介 面 , 這些介面分成以下 2 個體系:
  • 9. Collections Framework 簡介  Collection 介面:身為最上層的介面 , Collection 介面 具有相當的彈性 , 它可代表任何物件集合 , 這些物件可 以是有次序性或無次序性、有重複或沒有重複均可。此 介面定義了基本的集合操作方法 , 像是新增 / 移除集合 中的元素、將另一個集合新增進來或移除、將集合物件 轉成陣列的形式等等。  Set 及 SortedSet 介面: Set 代表的是沒有重複元素 的集合 , 和 Collection 介面一樣 , Set 介面也提供基本 的元素新增 / 移除等方法。 SortedSet 介面則是表示集 合中的元素會自動排序 , 因此 SortedSet 介面也多定義 了幾個與次序相關的方法 , 例如可傳回最前面或最後面
  • 10. Collections Framework 簡介  List 介面: List 代表有次序性 , 但元素可能有重複 的集合。 List 的元素有類似於陣列元素的索引碼 , 因此可透過 List 介面的方法 , 快速存取到指定的元 素、或某範圍內的元素。  Queue 介面:和 List 有點類似 , 也是有次序性及可 重複的集合 , 而其特色則是著重於佇列式的存取 , 也 就是加入元素時會加到最後面 , 而取出元素時則由最 前面取出。這就是所謂『先進先出』 (First In First Out) 的存取方式 , 稍後會再介紹。
  • 11. Collections Framework 簡介  Map 及 SortedMap 介面: Map 是個有『鍵 值』 (key-value) 對應關係的元素集合 , 例如每個學 號對應到一名學生 , 就是一種 Map 對應 , 其中 key 的值 ( 例如學號 ) 不能有重複。至於 SortedMap 介 面 , 則是代表會自動排序的 Map 集合。  有了基本的認識後 , 以下再分別介紹每個介面 , 以及 java.util 套件中實作該介面的類別。
  • 12. 18-1-2 Collection 介面與相關類別  如前所述 , Collection 介面提供了基本的集合操作方 法 , Set 與 List 類的集合類別都有實作這些方法 , 以便進行相關操作。  在實際使用各集合類別之前 , 我們先來認識一下 Collection 介面提供的基本方法 , 稍後介紹各集合類 別後 , 即可直接使用這些方法。  Collection 介面提供的操作方法可分為三類:基本管 理、大量元素的管理、及陣列的轉換。
  • 13. 基本管理方法  基本管理的方法有以下幾個:  除了 iterator() 方法外 , 其它幾個方法應該都很直覺 , 不必多加說明 , 至於 Iterator 介面與 iterator() 方法會 在本章稍後再加說明。 ▪ 請注意 , 只有『 參照型別』 的物件才能放入集合中。 基本型別的資料 (int 、 long 、 double 等 ) 則須先封 箱為基本類別 (Integer 、 Long 、 Double 等 ) 的物 件 , 然後才能存入集合中。
  • 14. 大量元素管理方法  為方便一次進行大量元素的增刪動作 , Collection 介 面提供以下幾個實用的方法:
  • 15. 陣列轉換  如果遇到需用陣列的方式來操作集合的情況 , 就可用 下列方法將集合內容存到一個全新的陣列中 , 然後用 該陣列進行處理。  請注意:產生的新陣列除了其元素值與集合元素相同 外 , 兩者並無其它關係 , 例如修改陣列元素值 , 並不 會影響到集合元素。
  • 16. Collections 類別  在 java.util 套件中 , 還有個名為 Collections 的類別。  它與 Collection 介面並無『直接』關係 , 此類別的主要 功能 , 是提供一些 static 方法 , 方便我們對集合物件進 行一些處理 , 雖然此類別的名稱是 Collections, 不過其 意思泛指所有的集合物件 , 而不限於只有實作 Collection 介面的類別 , 所以它所提供的部份方法也能用於 Map 類的集合物件。  Collections 類別提供的『工具』包括:搜尋、找出集合 中最大或最小的元素、將 List 集合排序或打亂次序等等 。  稍後我們會示範一些 Collections 類別提供的 static 方 法的用法。
  • 17. 18-1-3 重新定義 equals() 和 hashcode() 方法  在操作集合中的元素時 , 經常會需要比較二個物件是 否相等 , 例如使用前述的 contains(Object o) 來檢查 集合中是否包含 o 物件 , 這時就會使用物件繼承自 Object 類別的 equals() 來進行比較。  另外 , 為了提升在集合中尋找元素的效率 , 有些集合 類別會使用雜湊表 (Hash table) 的方式來存取元素 , 例如 HashSet 、 HashMap 等 ( 凡是名稱中包含 Hash 的類別都是 ) 。  這些類別在存取元素時 , 會呼叫物件繼承自 Object 類別的 hashcode() 來取得其雜湊碼 (Hash code), 然後以雜湊碼做為儲存或尋找元素的依據。
  • 18. 重新定義 equals() 和 hashcode() 方法  不過 Object 的 equals() 預設是以 == 做比較 , 而 hashcode() 則會傳回每個物件都不同的唯一整數序 號 ( 通常是由物件的位址轉換而來 ), 這二種方法都 只有當變數參照到「同一物件」時才會相等 , 因此都 不適用於集合 , 而必須重新定義 , 例如。
  • 19. 重新定義 equals() 和 hashcode() 方法
  • 20. 重新定義 equals() 和 hashcode() 方法
  • 21. 重新定義 equals() 和 hashcode() 方法  其實二物件是否相等 , 應由我們自行定義要如何比較 , 例如每個學生的學號都是唯一的 , 因此可用學號 (id) 做為比較的依據。  底下就來重新定義這二個方法:
  • 22. 重新定義 equals() 和 hashcode() 方法
  • 23. 重新定義 equals() 和 hashcode() 方法  以上除了 equals() 及 hashCode() 外 , 也順便實作 了 toString() 方法。  請注意 Object 類別的這 3 個方法均為 public, 而且 equals() 的參數必須定義為 Object 型別 ( 若改為 Student 則變成多重定義 ), 這樣才能正確地重新定 義。 ▪ Java 內建的基本類別 (Integer 、 Long...) 、 String 等 , 都已經重新定義好繼承自 Object 的相關方法了 , 所以我們可以直接使用。
  • 24. 關於雜湊碼的補充說 明  前面 Student 類別的識別資料 ( 學號 ) 剛好為 int, 所 以可以很方便地做為雜湊碼。  但如果識別資料是文字或其他較複雜的資料 , 例如學號 的格式為 "RA001" ( 其中的 RA 可能代表不同的年級 或班別 ) , 或是像某些軟體的序號為一堆文字 "SE23q0Xt9z45Ke81Puig" 。  序號越長 , 在搜尋資料時效率將越差 , 因為要比對的資 料會越多。  正因為這樣 , 所以才需要使用雜湊碼 , 也就是將一長串 的序號簡化為一個較短的整數 , 那麼用整數來搜尋時效 率就會快很多。
  • 25. 關於雜湊碼的補充說 明  至於轉換為整數的方法 , 其實怎麼轉都可以 , 只要符 合以下 3 個原則: 1. 同一個物件 , 每次傳回的雜湊碼都必須相同。例如甲學生 的雜湊碼每次傳回都要一樣 , 例如 4502 。 ( 但下一次執 行程式時 , 則允許和這一次的不同。 ) 2. 內容相等的物件 ( 就是用 equals() 比較為相等 ), 傳回的 雜湊碼也必須相等。反之 , 雜湊碼不相等時 , 其物件也必 須不相等。例如建立了 2 個甲學生物件 , 則其雜湊碼必 須都是 4502 ;如果某人的雜湊碼不是 4502, 那麼肯定 不是甲學生。 3. 內容不相等的物件 , 傳回的雜湊碼可以相等 , 也可以不等。 換句話說 , 雜湊碼相等時 , 其物件未必相等。例如甲和乙 的雜湊碼可以都是 4502 。
  • 26. 關於雜湊碼的補充說 明  其實雜湊表的搜尋原理 , 就是先用雜湊碼去搜尋 , 如果找到多個符合的物件 , 則再用 equals() 去找。  由此可知 , 當不同物件傳回的雜湊碼都不相同時 , 搜尋的效率最高;反之如果傳回的雜湊碼都相同 , 則搜尋效率最差 , 因為還必須再用 equals() 去一一 比對 , 這樣反而失去了使用雜湊碼的意義。
  • 27. 關於雜湊碼的補充說 明  所以我們在設計 hashCode() 時 , 應儘量讓每個物 件都不同 , 或是減少相同雜湊碼的物件數 , 以提升 效率。  不過 , 如果集合中的元素不多 , 那也就不必要求這 麼高了。 ▪ 即使所有物件都傳回相同的雜湊碼 , 該程式也可以是 正確的、合法的、甚至是適當的 ( 視需要而定 ) 。不 過正確的程式 ( 可以正常編譯、執行 ), 不一定是好 的程式。
  • 28. 18-1-4 Set 介面與相關類別  Set 介面代表的是元素內容都不重複的集合 , 換言之在 Set 類型的集合物件中 , 每個元素都是不同的。  Set 介面並未提供新的方法 , 所以其方法都是繼承自 Collection 介面。  由於『元素內容都不重複』 , 所以無法用 add() 、 addAll() 等方法將重複的元素加入 Set 類型 的集合物件中。  實作 Set 介面的類別為抽象類別 Abs tractSet , 它有 三個衍生類別: HashSet 、 LinkedHashSet 、 TreeSet 。  此外前面提過有個繼承 Set 介面的 SortedSet 介面 , TreeSet 即實作這個 SortedSet 介面。這些類別與
  • 29. Set 介面與相關類別 ▪ 凡是名稱中有 Hash 的集合類別 , 都 有使用前述的雜湊表來提升存取效率。 因此任何要存入 其中的物件 , 都必須 已實作繼承自 Object 的 hashCode() 方法。
  • 30. Set 介面與相關類別  各集合類別的建構方法都和陣列一樣可指定初始的大 小 , 例如以下就是建立可存放 10 個元素的 TreeSet 類別物件:  上列敘述設定的只是『初始』大小。稍後隨程式運作 需要 , 有更多元素加入集合時 , 集合的大小也會隨之 擴大 , 不像陣列一開始設定大小後就不能變動。  本章後續介紹的其它類集合類別 , 也都有相同的建構 方法可用 , 稍後就不再加說明。
  • 31. 建立集合物件的新語法  請注意 , 如果使用 5.0 版之後的 JDK, 上列敘述雖 仍可編譯成功 , 但編譯器會特別提出說明 (Note) 。  這是因為 JDK 5.0 新增了 Generics ( 泛型 ) 的功能 , 讓我們在建立集合物件時 , 可一併標明此集合將存 放何種類型的物件。  例如要存放字串的話 , 就要將程式寫成:
  • 32. 建立集合物件的新語法  請本節的程式仍採 5.0 以前的寫法 , 也就是不替集 合加上型別的宣告 , 以便將注意力放在集合上。  下一節則會為您詳細介紹泛型的各種應用。
  • 33. Set 介面與相關類別  以下就來介紹 HashSet 、 LinkedHashSet 、和 TreeSet 類別的用法。
  • 34. HashSet  HashSet 適用於任何希望元素不重複 , 但不在意其 次序性的集合。  要建立 HashSet 物件可先建立空的 HashSet 物件 , 再自行用 add() 方法將物件加入 , 或是用另一個集 合物件為參數呼叫建構方法。  除了繼承自 Collection 介面的各方法外 , HashSet 並未定義其它新的方法。
  • 35. HashSet  以下範例就是用 HashSet 設計一個文字接龍遊戲 , 程式會建立一個 HashSet 物件 , 而使用者每輸入一 個新詞 , 就會加到 HashSet 物件中。  由於 HashSet 中不能有重複的元素 , 所以使用者輸 入重複的詞 , 就無法被加到 HashSet 中 , 遊戲即結 束。  程式內容如下。
  • 38. HashSet 1. 第 15 行建立一個空的 HashSet 物件 words 。
  • 39. HashSet 2. 第 17 〜 24 行以 while 迴圈的方式執行將使用者 輸入字串加入集合 , 以及請使用者輸入下個字串的 動作。為簡明起見 , 本範例未如真的文字接龍一樣 , 檢查新輸入的字首與先前的字尾是否一致。 3. 第 18 〜 21 行以 if 執行 add() 方法將字串加入集 合 , 若 add() 傳回 false, 表示集合中已有相同內容 的物件 (Set 類型集合不允許重複的元素 ) 。此時就 會用 break 敘述跳出迴圈。 4. 第 27 行直接將 words 的內容輸出 , 由輸出結果可 發現 , 集合類別的 toString() 方法會自動在前後加 上一對方括號。
  • 40. HashSet  從上列程式最後輸出的結果可發現 , HashSet 物件中 元素的順序和加入時的順序並不相同 , 這即是 HashSet 『無次序性』的特性。  如果希望元素的次序能保持一致 , 則可改用 LinkedHashSet 。
  • 41. LinkedHashSet  LinkedHashSet 是 HashSet 的子類別 , 兩者的特性 也類似 , 只不過 LinkedHashSet 會讓集合物件中各 元素維持加入時的順序。  我們直接將上述的範例程式改用 LinkedHashSet 類 別即可發現:
  • 44. LinkedHashSet  請執行結果可發現 , 改用 LinkedHashSet 來建立集 合物件後 , 元素存放的順序就和加入時一樣。  這個程式最後面 , 我們故意用 toArray() 方法將集合 內容轉換成字串陣列 , 再以迴圈輸出所有字串 , 讓讀 者認識一下 toArray() 方法的用法。
  • 45. TreeSet  TreeSet 和前 2 個類別最大的差異 , 在於 TreeSet 實作了具有排序功能的 SortedSet 介面 , 一旦物件加 入集合就會自動排序。  也因為這個特點 , 此類別多了幾個和次序有關的方法 可用:
  • 46. LinkedHashSet ▪ 如果集合中的元素是基本型別 , 例如 int, 那麼 first() 及 last() 傳回的 Object 資料不可用 int 強制轉型 ( 因為 Object 只能轉型為參考型別 ) 。此時必須先強 制轉型為 Integer , 然後就會自動拆箱 (Unboxing) 為 int 。  如果元素是 String 或數值類的基本型別 , 那麼就會 依照字母或數值由小到大排序 ( 稱為自然順序 ) 。  但若是自訂的類別 , 則必須由我們自行定義排序方式 , 相關方法請參見 18-1-10 節。
  • 48. LinkedHashSet 1. 第 3 行建立一個字串陣列 , 並在第 6 行以 Arrays 類別的 asList() 將之轉換為 List 集合 , 然後加入 ts 集合中。 2. 由程式輸出的結果 , 可看出「空白 < - < 0~9 < A~Z < a~z < 中文字」 , 請記住這個自然順序。  要特別注意的是 xxxSet() 方法傳回的子集合 , 仍 『屬於』原始的集合物件 , 所以若改變子集合的內 容 , 原集合的內容也會改變 , 請參考以下的程式範 例。
  • 51. LinkedHashSet 1. 第 8 〜 10 行建立一個新的 TreeSet 物件 , 隨後 以迴圈將 10 到 100 間 10 的倍數之整數物件加 到其中。請注意 , 在第 10 行加入元素時 , add(new Integer(i*10)) 也可寫成 add(i*10), 因為 int 在必要時會自動封箱 (Autoboxing) 為 Integer 物件。 2. 第 14 行取得小於 50 的元素所形成的子集合。並 於第 16 行用 clear() 方法將之清除。 3. 第 11 、 18 行分別輸出移除子集合前後的母集合 大小 , 以供比較。
  • 52. LinkedHashSet  如執行結果所示 , 當我們移除子集合後 , 母集合的元 素也跟著變少了 , 這就表示 xxxSet() 方法傳回的子 集合 , 仍是原始集合物件的一部份。
  • 53. 使用子集合的注意事項  前面使用 headSet 、 subSet 、 tailSet 所傳回的子 集合稱為後備集合 (Backed Collection) 。  當我們異動的元素落在後備集合的範圍時 , 原始集合 與後備集合都會同時被異動 , 而當新增一個超出範圍 的元素到後備集合中時 , 則會丟出例外!例如在前面 程式的後面加入:
  • 54. 18-1-5 List 介面與相關類別  List 適用於有次序性、但元素可能重複的集合。 List 集合在使用上和陣列有些類似 , 因為 List 集合中的 元素 , 也都可透過索引 (index) 來存取。  因此 List 介面定義了一些與索引有關的方法 , 例如 除了繼承自 Collection 介面的 add() 、 remove() 的新增移除元素方法 , 也增加了可指定索引值來新增 或移除元素的方法。
  • 56. List 介面與相關類別  上列的 add() 和 set() 方法感覺上都是將第 index 元素設為物件 element, 但其實兩者的意義差別很大 。  set() 是將原位置上的物件『取代』掉;但 add() 則 是在指定位置加入新物件 , 而原物件則是往後移。如 下圖:
  • 57. List 介面與相關類別  在 java.util 套件中實作 List 界面的只有 AbstractList 抽象類別 , 其衍生類別則有 AbstractSquentialList 、 ArrayList 、 LinkedList 等 , 其關係如下:
  • 58. ArrayList  我們可將 ArrayList 看成是個伸縮自如的陣列 , 原本 的陣列在宣告之後 , 元素的數量就固定了 , 不能再增 加元素的個數 , 此外若要在陣列中插入一個元素 , 並 讓原來的元素都向後移一個位置 , 也必須自已做相關 處理 , 相當不便 , 但使用 ArrayList 就不會這麼麻煩 了。  然而不斷地變動大小、或配置過多未用的空間對程式 / 系統的效能也有些負面影響 , 因此 ArrayList 也提 供了 2 個與使用空間有關的管理方法:
  • 61. ArrayList 1. 第 8 行建立一個新的 ArrayList 物件 , 隨後以迴圈將字元陣 列的內容加到其中。 2. 第 12 、 13 分別是先輸出集合目前大小 , 再輸出所有的元 素。 3. 第 15 〜 17 行用 add() 方法在指定位置加入新元素 , 舊元 素則向後移。 4. 第 21 、 22 行是故意換用迴圈呼叫 Collection 類別的 get()
  • 62. ArrayList  從上列程式第 1 次的執行結果可發現 , 在不指定索 引的情況下 , 以 add() 方法加入的元素都是依序加 到集合中『最後面』 , 所以一開始加入的元素排列順 序 , 和陣列的內容相同。  但之後用另一個 add() 方法 , 同時指定了加入元素 的索引 , 元素就只會加到我們指定的位置 , 而原位置 及其後的元素都依序向後移一位。
  • 63. ArrayList  當您想使用陣列來處理資料 , 但在程式執行過程中可 能需移除或增加陣列元素 , 就可先用 ArrayList 來建 立集合 , 待一切底定、元素數量都不會更動後 , 再呼 叫繼承自 Collection 介面的 toArray() 方法將其轉 成陣列。
  • 64. LinkedList  LinkedList 是另一類型的 List 類別 , LinkedList 的 特點是它提供了一組專門處理集合中第一個、最後一 個元素的方法:
  • 65. LinkedList  由於上述的特性 , LinkedList 很適合用來實作兩種基 本的『資料結構』 (Data Structure) :即堆疊及佇列 。  所謂堆疊 (stack) 是指一種後進先出 (LIFO, Last In First Out) 的資料結構 , 加入此結構 ( 集合 ) 的物件 要被取出時 , 必須等其它比它後加入的物件全部被拿 出來後 , 才能將它拿出來。
  • 67. LinkedList  至於佇列 (queue) 則是一種『先進先出』 (FIFO) 的資料結構 , 像日常生活中常見的排隊購物 , 此隊伍 就是個先進先出的佇列:
  • 68. LinkedList  對資料結構有興趣者 , 可參照其它相關主題的書籍。  初學程式語言者可先記住 , java.util 套件已事先設計 好 LinkedList 類別 , 當您日後遇到需使用堆疊或佇 列的資料結構的場合 , 就可直接用 LinkedList 來設 計程式 , 不必自行從頭設計資料結構。  以下就是一個簡單的 LinkedList 應用範例。
  • 72. LinkedList 1. 第 15 行建立一個新的 LinkedList 物件。 2. 第 16 〜 26 行的 for 迴圈逐一檢查輸入字串中所 含的左右括號。 3. 第 17 〜 18 行讀到左括號時就用 addFirst() 方法加 入一個新元素。 4. 第 19 〜 26 行讀到右括號時就用 removeFirst() 方 法移除一個元素。
  • 73. LinkedList 5. 第 23 〜 26 行 , 若發生 NoSuchElementException 例外 , 表示集合中已無 元素 , 但仍有右括號 , 表示右括號是多出來的 , 或 先前漏打了一個左括號。 ▪ LinkList 類別同時實作了 List 及 Queue 介面 , 因 此也具備 Queue 介面的 offer() 、 TIIP poll() 、 peek() 等方法 , 這些稍後即會介紹。
  • 74. 18-1-6 Queue 介面與相關類別  Queue 介面主要是實作『先進先出』 (FIFO, First In Fisrt Out) 的集合存取方式 , 提供了以下 6 種方 法來存取元素 , 前 3 種是會丟出例外的版本 , 而後 3 種則不會:
  • 75. Queue 介面與相關類別  實作 Queue 介面的類別 , 除了前述的 LinkList 類 別 ( 同時實作 List 及 Queue 介面 ) 外 , 常用的還 有 PriorityQueue :
  • 76. PriorityQueue  PriorityQueue 會依照特定的優先順序 (Priority) 來 排序 , 高優先的元素會排在較前面 , 因此會優先被取 出 (FIFO) 。  優先順序可以是自然順序 ( 依字母或數值由小到大 排 ), 也可以自訂順序 ( 例如使用 Comparotor 來排 序 ), 有關排序的說明請參閱第 18-1-10 節。底下是 依整數排序的佇列 ( 越小越優先 ) 。
  • 78. 18-1-7 Map 介面與相關類別  Map 介面是用來存放『鍵值對』 (key-value pair) 對應關係的元素集合 , 在加入元素時 , 必須指定此元 素的 key 及 value 兩項內容 , 而且 key 的內容不 可重複。  例如有個學生的集合 , 就可用學號為『鍵』、學生姓 名為『值』。  如果在加入元素過程中 , 加入了重複的『鍵』 , 則新 的值會取代舊的值。
  • 79. Map 介面與相關類別  由於上述的特性 , 因此 Map 介面只有 isEmpty() 、 size() 這兩個方法和 Collection 的同名方法相似 , 而 元素的新增 / 移除需改用下列方法:
  • 80. Map 介面與相關類別  至於整個 Map 集合的操作則有以下的方法可使用:
  • 81. Map 介面與相關類別  至於 SortedMap 介面除了繼承 Map 介面外 , 還提 供以下幾個類似於 SortedSet 的方法:
  • 82. Map 介面與相關類別  Map/SortedMap 的相關類別關係如下:
  • 83. Map 介面與相關類別  AbstractMap 和 AbstractCollection 一樣定義了 toString() 方法 , 所以也可呼叫 System.out.println() 方法直接輸出 Map 類型物件的內容。  雖然 AbstractMap 有 5 種衍生類別 , 但一般只用 到 HashMap 、 TreeMap 和 LinkedHashMap , HashMap 就像 Set 中的 HashSet, 是用來存放不 需強調先後次序的鍵值對集合。
  • 84. Map 介面與相關類別  至於對應的 TreeMap 也和 TreeSet 一樣 , 會自動 將集合中的鍵值對排序 ( 預設是以『鍵』物件的排序 為準 ) , 所以適合用來存放需排序的鍵值對 , 除了用 鍵搜尋值的執行效率較佳另外 , 也提供 headMap() 、 subMap() 、 tailMap() 來取出子集合 ( 後備集合 ) 。  至於 LinkedHashMap 則和 LinkedHashSet 類似 , 會維持鍵值對加入時的順序。  以下就是一個簡單的 HashMap 範例。
  • 87. Map 介面與相關類別 1. 第 12 行建立一個新的 HashMap 物件。 2. 第 14 、 15 行的 for 迴圈逐一將 cities 陣列的內 容以鍵值對的方式加入 HashMap 物件中。 3. 第 20 、 21 行加入新的鍵值對並輸出內容。 4. 第 24 行用重複的鍵加入鍵值對 , 結果會取代掉原 有的鍵值對。
  • 88. 古老的 Vector 及 Hashtable 類別  ArrayList 和 HashMap 各有一個歷史悠久的相同類別:  這二個古老類別的特別之處 , 就是為『 多緒安全』 (Thread safe) 的 , 因為其主要的方法都已設定同步化 (Synchronized) 。  其中 Hashtable 的 t 沒有大寫 ( 由此可見其歷史的古 老 ), 並且和 HashMap 還有一個差別 , 就是其 key 與 value 都不允許為 null, 而 HashMap 則允許有一個 null 的 key, 以及不限數目的 null value 。 ▪ 這二個古老類別也在 SCJP 的考試範圍 , 所以請記住 新、舊類別的對應關係及差別。
  • 89. 18-1-8 Iterator 迭代器  Iterator ( 迭代器 ) 是 java.util 套件中的另一個介面 , 它並不是用來建立集合 , 而是用來逐一瀏覽集合中所 有元素的一項工具。  所有 Collection 類別都有個  iterator() 方法 , 以集合物件呼叫此方法可傳回一 Iterator 物件 , 我們可用此物件呼叫 Iterator 介面的 方法來逐一取得、移除集合中的元素。  Map 型的類別則無 iterator() 方法 , 但我們可用 18-26 頁所列的 entrySet() 等方法取得代表 Map 物件的集合物件 , 再用它建立 Iterator 物件。
  • 90. Iterator 迭代器  Iterator 介面提供了 3 個瀏覽集合物件的方法:  以下我們稍稍修改前面的 SubTree.java 範例程式 , 示範 Iterator 介面的用法。
  • 93. Iterator 迭代器 1. 第 8 〜 10 行建立一個 TreeSet 物件並將 1 到 100 的整數存入其中。 2. 第 14 行建立 Iterator 物件 i 。 3. 第 16 〜 18 行用 while() 迴圈逐一讀取所有元素 , 並檢查該元素值是否可被 9 整除 , 不行就將元素移 除。 4. 第 20 行輸出最後的集合內容 , 就只剩下 9 的倍 數。
  • 94. For-Each 迴圈  在介紹陣列時 , 曾簡單介紹過 For-Each 迴圈這個 JDK 5.0 新增語法的用法 , 其實 For-Each 迴圈也 非常適合用於集合物件上。  For-Each 迴圈用於集合時 , 要指定一個您要『逐一 讀取所有元素』的集合物件 , 以及一個代表單一元素 的變數 , 其它部份則和 for 迴圈相同。 For-Each 迴 圈的語法如下:
  • 95. For-Each 迴圈  底下直接修改前一個範例程式 , 來示範 For-Each 迴 圈的用法:
  • 97. For-Each 迴圈 1. 第 8 行使用本章開頭介紹的 JDK 5.0 新語法 , 以 <Integer> 標示 TreeSet 集合是用來存放整數物件 。 2. 第 15 即為 For-Each 迴圈 , 括號中前半宣告一個 Integer 物件 i, 後半則是集合物件 IntTS 。所以 For-Each 迴圈的作用就是:『從頭開始 , 依序取出 IntTS 中的每個元素』 , 而每一輪迴圈中可用物件 i 來代表該輪迴圈正在使用的元素。
  • 98. 18-1-9 集合的排序  前面介紹的 TreeSet 、 TreeMap 、及 PriorityQueue 都是屬於會自動排序的集合類別 , 它 們通常會使用自然順序 (Natural order) 來排序 , 例 如 String 就是依照字母的順序 (A 小於 B, 若第一 個字母相同就比第二個 ), 而 int 則是依照數值大小 , 由小到大排序。  但如果集合中的元素是我們自行定義的類別 , 那麼這 個自然順序就必須由我們自行定義 , 否則會編譯錯誤 , 因為 Java 也不知道要如何排序。
  • 99. 集合的排序  設定類別的排序方法有二種 , 第一種是讓類別實作 Comparable 介面的 compareTo() 方法 , 此方法其 實就是在定義『比較大小』的方法 , 也就是定義類別 的自然順序。  另一種方法則不須更改原來的類別 , 而是另外定義一 個實作 Comparator 介面的類別來幫忙排序。
  • 100. Comparable 介面:讓類別具備排序能力  只要讓類別實作 Comparable 介面 , 並重新定義介 面的 CompareTo() 方法 , 即可讓該類別具備排序能 力。底下來看範例:
  • 102. Comparable 介面:讓類別具備排序能力  第 10~11 行就是在重新定義 Comparable 介面的 compareTo() 方法 , 注意必須宣告為 public int, 而 傳回值若為 0 表示相等 , 大於 0 表示物件較大 ( 大於參數傳入的物件 ), 小於 0 則物件較小。 ▪ Comparable 也有泛型的版本 , 例如將以上的介面改 為 Comparable<Student>, 則 compareTo(Objet o) 就可改為 compareTo(Student o), 那麼在方法中就不 需再用 (Student) 來將 o 轉型了。 ( 有關泛型請參閱 18-2 節 )
  • 103. Comparable 介面:讓類別具備排序能力 2. 在 compareTo() 中要比較的資料若是 String 、 Integer 等基本型別 , 那麼也可使用其內 建的 compareTo() 方法來比較 , 例如第 11 行也 可改為: 3. 第 17~20 行分別加入 3 個學生然後印出排序結果 。
  • 104. Comparable 介面:讓類別具備排序能力  請注意 , 對於任何要排序的集合來說 , 其內部元素的 型別最好都相同 , 否則在呼叫元素的 CompareTo() 比較大小時 , 可能會發生無法轉型的錯誤。例如:  以上在 TreeSet 集合中先存入 5, 然後再存入 6L 時丟出了 ClassCastException 例外。  這是因為存入 6L 時 , 會自動呼叫 6L (Long) 的 compareTo(Long 其他元素 ) 來排序 , 但其他元素 (5, 為 Integer) 則因無法轉型為 Long 而發生錯誤 。
  • 105. Comparator 介面:定義幫 忙排序的類別  實作 Comparable 介面時必須更改原始的類別 , 如 果不想 ( 或不能 ) 更改原始類別 , 或是想要建立多種 排序方式以供不同的狀況使用 , 那麼就可改用 Comparator 介面 , 另外定義新的類別來幫忙排序。  底下程式定義了二種可幫忙 Student 排序的新類別 :
  • 107. Comparator 介面:定義幫 忙排序的類別 1. 第 13 及 17 行分別定義了二個幫忙排序的新類別 , 並實作了 Comparator 介面的 compare() 方法 , 此方法須傳入 2 個要比較的物件 , 而傳回值則與 Comparable 介面的 compareTo() 一樣。 ▪ Comparator 也有泛型的版本 , 例如將以上的介面改 為 Comparator<Student>, 則 compare() 中的參數 就可改為 compareTo(Student o1, Student o2), 那麼 在方法中就不需再用 (Student) 來將 o1 、 o2 轉型 了。 ( 有關泛型請參閱 18-2 節 )
  • 108. Comparator 介面:定義幫 忙排序的類別 2. 第 23 及 28 行分別利用新建立的排序物件來建構 TreeSet 物件 , 因此會都以排序物件中所定義的 comare() 方法來排序。 有了這種功能 , 我們就可以預先定義多種不同的排 序類別 , 然後視需要而選用不同的排序方式。下面 是各自動排序集合的相關建構方法:
  • 109. 18-1-10 Collections 和 Arrays 類別  Collections 類別與 Arrays 類別中包含了許多實用 的靜態方法 , 可分別針對集合與陣列進行排序、搜尋 、顛倒順序 ... 等操作。  其中的 Collections 類別請不要和 Collection 介面 弄混了 , 二者並沒有直接的關係。
  • 110. Collections 和 Arrays 類別  Collections 的排序與搜尋功能 , 主要是針對 List 所 設計 , 例如 ArrayList 類別。  而 Arrays 類別則主要是針對陣列進行操作 , 下面列 出這二個類別的常用功能:
  • 112. Collections 和 Arrays 類別  底下先建立 Book 類別及其排序類別 (Book.java), 然後再示範排序功能:
  • 114. Collections 和 Arrays 類別  以上在 Book 類別中定義了繼承自 Object 的 toString() 、 equals() 、及 hashCode() 方法 , 也定 義了比較大小的 compareTo() 方法來實作其自然順 序。  BookByName 類別則是可依書名遞增排序的類別。 接著就使用這些類別來撰寫排序程式。
  • 117. Collections 和 Arrays 類別 1. 第 4~9 行是陣列的建立及排序 , 第 11 行將陣列 轉為 List 來建構 ArrayList 物件 , 然後進行 2 種 排序。 2. 第 17~21 行示範 List 的反轉排序 , 第 23~27 行 則示範 List 的二分搜尋。
  • 118. Collections 和 Arrays 類別  使用 Collections.binarySearch() 來搜尋時 , 必須注 意以下幾點: ▪ 被搜尋的陣列或 List 必須已排序過。若是依自然順序 排 , 則可直接搜尋;但如果是使用自訂的 comparator 排序 , 則在 binarySearch() 的參數中也必須加上相同 的 comparator 物件。 ▪ 如果搜尋到了 , 會傳回元素的位置 ( 大於等於 0 的 int) 。 ▪ 如果沒找到 , 則傳回一個負數 , 代表插入點的位置 ( 就是搜尋的 key 若插入 List 中 , 所應插入的位 置 ) 。當傳回 x 時 , 則插入點位置為 (-x-1), 例如上 例中搜尋 103 時傳回的 -2, 就表示其插入點應為 (-(2)-1) = 1 。
  • 119. 18-1-11 TreeSet 和 TreeMap 的導航  自 Java 6 開始 , 針對 TreeSet 和 TreeMap 分別 新增了導航 (Navigating) 的 NavigatableSet 及 NavigatableMap 介面:
  • 120. Collections 和 Arrays 類別  在 NavigatableSet 介面中增加了以下方法:
  • 121. Collections 和 Arrays 類別  在 NavigatableMap 介面中也增加類似的方法 , 但 在方法的名稱中多了 Key 或 Entry :  底下程式分別示範 Java 5 及 Java 6 的寫法 , 以展 示新導覽介面的效用。
  • 123. Collections 和 Arrays 類別 1. 第 8 、 9 行是 Java 5 的寫法 , 必須先用 headSet() 取出小於 140 元的子集合 , 然後再用 last() 取出子集合的最後一個元素。 2. 第 12 行是 Java 6 的寫法 , 直接用 lower() 來讀 取即可。
  • 124. Collections 和 Arrays 類別  另外 , NavigatableXxx 介面 (Xxx 表示 Set 或 Map) 也多重定義了 headXxx() 、 tailXxx() 、 subXxx() 方法 , 在這方法 中多加了一個 boolean 參數 , 代表傳回的子集合中 是否要『排除』做為分界的元素 , 例如: ▪ 有加 boolean 參數的方法會傳回 NavigatableXxx 型 別的集合 , 沒加 boolean 參數時則傳回 SortedXxx 型別的集合。
  • 125. 18-2 泛型 (Generic Types)  當我們在宣告陣列時都一定會指定型別 , 因此陣列是 型別安全 (Type Safe) 的 , 因為只有符合型別的資 料才能存入陣列中。  也就是說 , 我們無法將 String 物件存入 Integer 的 陣列中 , 因為型別不符。  然而 , 當我們在存取集合時 , Java 就無法幫我們檢 查型別了 , 因為集合預設是可以接受任何物件 ( 即 Object 及其衍生類別 ) 的。
  • 126. 18-2-1 使用泛型集合  為了讓集合 ( 以及其他我們自訂的類別 ) 也可以是型 別安全的 , 自 Java 5 開始加入了泛型 (Generic Types) 的功能 , 我們只要在集合類別名稱之後加上 "< 型別 >", 就可讓該集合只能存入指定型別的資料 , 例如:
  • 127. 使用泛型集合  像以上緊接在類別之後的 < 型別 > 就是泛型型別 , 也稱為型別參數 (Type parameter) 或參數化型別 (Parameterized type), 因為它就像是類別的參數一樣 。  一旦宣告了集合的泛型型別 , 那麼在讀取集合元素時 , 也不需要再做轉型的動作了 , 例如:
  • 128. 使用泛型集合  事實上 , 編輯器知道 tm.get(1) 會傳回 String 型別 , 因此不但不用轉型 , 而且還會幫我們檢查型別。  所以如果使用 int 變數來接收傳回值 , 就會發生編譯 錯誤。
  • 129. 在參數及傳回值 使用泛型集合  在使用泛型集合時 , 除了宣告及建構物件時要加型別 參數外 , 自訂方法的參數或傳回值也可加上型別參數 。  例如底下的 newBook( ) 方法可接收及傳回 ArrayList<Book> 集合 (Book 類別定義在 Book.java, 參見 8-1-10 節 ) 。
  • 132. 18-2-2 混合使用泛型與非泛型的集合  為了與舊版的程式相容 , Java 允許我們混合使用泛 型與非泛型的集合 , 例如以下程式是合法的:
  • 134. 混合使用泛型與非泛型的集合 1. 第 5 、 6 行將泛型集合與非泛型的參照相互指定 , 並在第 10 行將 newBook 方法的參數及傳回值都 改為非泛型集合 , 然後以泛型集合 b 為參數來呼叫 , 這些寫法都是合法的。 2. 在第 14 行讀取非泛型集合的元素時 , 記得必須先 強制轉型為 Book 後才能使用。
  • 135. 混合使用泛型與非泛型的集合 3. 凡是使用了『非泛型』集合的程式 , 在編譯時都會 出現二行 Note 警告 , 第一行是提示您使用了未檢 查或不安全 ( 型別 ) 的操作 , 第二行則建議您使用 -Xlint:unchecked 參數重新編譯 , 以便顯示較詳細 的相關說明。 不過這些都只是警告而已 , 程式仍可正常編譯及執 行。
  • 136. 18-2-3 泛型的『型別消除』特性與注意事項  由於 Java 只有在『編譯時』才會檢查及處理泛型的 型別 , 然後會經過一個型別消除 (Type erasure) 的 過程 , 將型別參數由程式碼中消除。  因此在 .class 的二元碼 (Byte code) 中完全沒有泛 型的資訊 , 而在執行時自然也就無法幫我們檢查型別 參數的正確性了。
  • 137. 泛型的『型別消除』特性與注意事項  因此 , 混用泛型與非泛型的集合時 , 有可能會發生一 些危險的狀況 , 例如修改以上的 newBook() 方法:
  • 138. 泛型的『型別消除』特性與注意事項  以上程式宣告一個 TreeSet<Book> 的集合物件 , 但 呼叫 newBook() 時卻在其中加入一個 String 元素 , 此時由於方法的參數為 TreeSet ( 非泛型的集合 ), 所以編譯器不會發現問題。  而在編譯成功並執行到第 6 行時 , 則會因取出來的 元素無法轉換為 Book 型別而丟出例外。
  • 139. 泛型的『型別消除』特性與注意事項  也就是說 , 程式執行到第 10 行將錯誤的 String 物 件加入集合時 , JVM 並不會發現錯誤而丟出例外!  要一直等到稍後讀取該錯誤元素時 , 才會因型別轉換 錯誤而丟出例外。  但如果程式較為複雜 , 或是很久以後才讀到該錯誤元 素 , 那麼我們將很難找出錯誤發生的地點。因此最好 盡量少用非泛性的集合。 ▪ 陣列和泛型集合不同 , 陣列在編譯和執行時都會檢查 型別 ( 而不會型別消除 ), 因此即使將 Integer 陣列當 成 Object 陣列來操作 , 在執行時也不允許將 String 資料加入陣列 ( 會丟出 ArrayStoreException 的例
  • 140. 18-2-4 泛型與多型  我們知道 Java 具備『多型』的特性 , 也就是可以將 子類別的物件指定給父類別 , 例如:  那麼泛型是否也具備多型的特性呢?  其實泛型的整個型別可分為二部份 , 以 TreeSet<Long> 為例 , 前面的 TreeSet 稱為基底型 別 , 而 <> 中的 Long 則為泛型型別。
  • 141. 泛型與多型  基底型別具備多型的特性 , 而泛型型別則不具備 , 也 就是說 , 泛型型別必須完全一樣才行。例如:
  • 142. 泛型與多型  對集合來說 , 泛型的型別其實就是集合中元素的型別 , 因此需要嚴格的限制 , 否則一旦讓元素為 Integer 的集合變成元素為 Object 的集合 , 那麼編譯器將無 法阻止任何的非法存入了。  不過請不要弄混了 , 當我們對集合中的元素操作時 , 則是允許多型的。
  • 144. 泛型與多型 ▪ 如果是自動排序的集合 , 則所有元素都必須可相互比 較大小才行。例如若將上面的 ArrayList 換成 TreeSet, 則執行到第 6 行時會丟出 ClassCastException 例外 , 因為無法將 5 (Integer) 轉為 Long 來呼叫 6L 的 compareTo(Long) 方法。
  • 145. 18-2-5 泛型的通用型別  雖說泛型型別不具備多型的特性 , 但在某些情況下確 有多型的必要性 , 例如我們要寫一個反向排序的方法 , 其操作對象可以是 TreeSet<Integer> 、 TreeSet<Long>... 等元素為基 本型別的 TreeSet 集合 , 那麼就可使用通用型別 (Wildcard type) : <?> 來定義:
  • 147. 泛型的通用型別 1. 第 2 行以通用泛型型別 List<?> 來定義 sortList() 的參數及傳回值。 2. 第 7 、 11 、 15 行則分別建立不同泛型型別的 ArrayList 集合 , 然後存入資料、進行排序、及列印 內容。
  • 148. 限制通用型別的範圍  以上的 sortList() 如果希望只能接受泛型型別為 Number 或其衍生類別的參數 , 則可改寫為:  那麼前面第 17 行就會編譯錯誤了 , 因為 String 不 是 Number 的衍生類別。  請注意 extends 後面可以是一個類別 , 也可以是一 個介面 , 但千萬不可以將 extends 改為 implements 。
  • 149. 限制通用型別的範圍  另外要注意的是 , List<Object> 和 List<?> 是不同的 。  前者只允許參照或傳入 List<Object> 型別的集合 , 而後者則可以是 List< 任何型別 >, 例如 List<String> 或 List<Book> 都可以。
  • 150. 通用型別的限制與 super  使用通用型別有一個限制 , 就是不允許在通用型別的 集合中存入資料!  這當然是為了要維護型別安全 , 例如在上面的 sortList() 中增加一行:
  • 151. 限制通用型別的範圍  那麼在編譯時 , 第 3 行會發生 cannot find symbol 的錯誤 , 表示 aList 不能用 add() 來加入資料。  此時如果編譯器不阻止的話 , 萬一傳入的是 ArrayList<Long> 集合 , 那麼在方法中加入 Integer 資料又會發生難以查覺的錯誤了。  不過 , 如果真的需要在方法中加入某型別的元素 , 那 麼可將參數改為 <?super 某型別 > 的寫法 , 表示 只能傳入某型別或其上層型別 , 例如。
  • 153. 限制通用型別的範圍  如果傳入的集合其泛型型別都是 Integer 或其上層型 別 ( 例如 Number 或 Object), 那麼在方法中用 add() 加入 Integer 資料就不會有問題 , 所以可以通 過編譯器的檢查。  但如果傳入了 ArrayList<String> 或 ArrayList<Book> 集合 , 則會因不符 super 的限制 而編譯錯誤;同理 , 若在方法中加入非 Integer 的資 料 , 那麼也會編譯錯誤。 ▪ 以上程式雖說不會有問題 , 但若傳入方法的是 ArrayList<Number> 集合 , 則在排序時要小心 , 因為 其內若存放著 Integer 、 Long 等不同型別的元素 , 那麼在排序時就會發生錯誤 ( 丟出例外 ) 。
  • 154. 限制通用型別的範圍  最後要注意的是通用型別只能用在宣告上 ( 變數、參 數、及傳回值 ), 而不可用來建構物件 , 因為物件一 定會有固定的型別。底下是一些範例:
  • 155. 18-2-6 建立泛型類別  在學會使用泛型集合之後 , 其實我們也可以定義自己 的泛型類別。首先來查看一下集合類別的 API 文件 , 底下以 TreeSet 為例:
  • 156. 建立泛型類別  其中的 E 就代表泛型的型別 , 當我們宣告 TreeSet<String> 物件時 , 其相對的父類別就是 AbstractSet<String>, 而其建構方法及一般方法中的 E 也都會換成 String 。例如要加入元素時 , 就必須 呼叫 add(String e) 的方法。
  • 157. 建立泛型類別  以上的 E 其實只是一個代表泛型型別的名稱 , 您也 可以換成其他合法的識別字 , 例如 T 、 K 、 GenType.. . 等 , 只要不和其他識別字重複 即可。  在集合中的泛型名稱通常會使用 E, 代表其為 Element( 元素 ) 的型別;在其他地方則習慣以 T 表 示 , 就是 Type 的意思。
  • 158. 定義泛型類別  若要定義自己的泛型類別 , 只要在類別名稱後面加上 <T>, 然後在類別中以 T 來代表泛型型別即可。例如 底下定義一個簡易版的商店類別:
  • 159. 定義泛型類別  其中的 T 就代表商品的型別 , 例如要開書店 , 就可 用前面設計過的 Book 類別。  一旦在類別名稱之後加上了 <T>, 那麼 T 這個型別 就可在類別中任意使用了 , 等到將來實作物件時 , 類 別中所有的 T 都會以指定的泛型型別 ( 例如 Book) 取代。 ▪ 泛型型別也可以有多個 , 例如 class StoreEz<T,U>, 則會有 T 、 U 二個泛型型別 , 均可出現在類別的定 義中。當然 , 在使用此類別時也須指定二個型別 , 例 如 StoreEz<Book,String> 。
  • 160. 定義泛型類別  以上類別宣告了一個 List<T> 來參照商品列表 , 在 建構方法中則須傳入該商品列表。  get(index) 會傳回列表中 index 位置的 T 型別商品 , sale(T) 會將指定商品自列表中移除 , 而 report() 則可列出商品清單。底下就是書店程式:
  • 162. 定義泛型類別 1. 第 5~8 行建立商品清單並存入 3 本書。然後在第 10 行用此清單來建構商店物件 , 接著印出店內的商 品清單。 Book 類別的程式碼請參見第 18-1-10 節 。 2. 第 13~16 行取出第 2 本書 ( 索引為 1), 然後賣 掉 , 最後印出店內的商品存貨。
  • 163. 限制泛型型別的範圍  如果想讓前面的商店類別也能計算金額 , 可先讓每種 商品類別都提供 getPrice() 方法來傳回訂價 , 如此 一來 , 在商店類別中就可呼叫商品的 getPrice() 來 計算價錢了。  然而 , 如果在商店類別中直接呼叫 getPrice(), 此時 由於商品的型別 (T) 未確定 , 因此編譯器會視為錯 誤 , 因為無法保證未來的商品一定有此方法。  為了保證未來的商品都有 getPrice() 方法 , 最好的 做法 , 就是定義一個具備此方法的介面 , 然後限定只 有此介面的衍生類別才能做為泛型型別。
  • 166. 限制泛型型別的範圍  第 3 行就是可供商品類別實作的 Salable 介面 , 其 內只宣告了 getPrice() 方法。  在第 6 行定義泛型類別時 , 則限定所有的 T 都必 須衍生自 Salable 介面 , 如此一來 , 在第 19 行呼 叫商品的 getPrice() 方法時 , 編譯器就會開心地讓 我們過關了。  另外 , 在類別中也多加了一個 balance 變數來代表 店內現金 , 並在相關方法中加入處理現金的程式。  底下我們就另外開一家 T 恤商店吧。
  • 169. 限制泛型型別的範圍 1. 第 2 行是定義商品類別: Tshirt, 此時必須實作 Salable 介面 , 否則無法放在 Store 中販賣 ( 因為 Stroe 的泛型型別只接受 Salable 的衍生類別 ) 。 2. 第 9 行實作了 Salable 介面的 getPrice() 方法。 其他的程式都和前面的簡易版商店差不多 , 就不再 多做解釋了。
  • 170. 18-2-7 建立泛型方法  有時我們只想將泛型使用在某個方法之中 , 而不需要 套用在整個類別 , 這時候就可以定義泛型方法。  做法很簡單 , 只要將 <T> 放在方法的傳回型別之前 , 那麼就可將 T 用在方法的參數、內容、及傳回型 別之中了。  例如底下是可產生 ArrayList 並存入二個元素的泛型 方法:
  • 172. 建立泛型方法 1. 第 3 行就是在定義泛型方法 , 請注意 <T> 必須放 在傳回型別 TreeSet<T> 之前。加上 <T> 之後 , T 就可任意出現在方法的參數、內容、及傳回型別之 內了。 2. 第 10 行呼叫泛型方法 , 這裡要注意的是 , 『傳入 參數的型別』就會變成泛型型別 , 也就是會取代 T 的型別。此處傳入 String 物件 , 因此會建立 TreeSet<String> 的集合。
  • 173. 建立泛型方法 3. 第 12 行則以 Book 物件呼叫泛型方法 , 因此會建 立 TreeSet<Book> 的集合。有關 Book 類別的程 式碼請參見第 18-1-10 節。 ▪ 在宣告泛型類別的變數、參數、或傳回值時 , 可以用 <?> 來表示通用泛型型別;但在定義泛型類別或泛型 方法時 , 則不可使用 <?>, 而必須使用如 <E> 、 <T> 等識別字來代表型別參數。 ▪ 類別名稱、型別參數名稱、與變數名稱分屬不同的命 名空間 , 因此名稱重複時並不會造成衝突。例如 class T { <T> void T(T T) { } ; } 是合法的 , 雖然有點 荒謬。 ( 這 5 個 T 分別是:類別名稱、泛型型別、 建構方法名稱、泛型型別、參數名稱。 )
  • 175. 1. Given: What is the result? A. b,a,c, B. c,a,b, C. c,b,a, D. Compilation fails. E. An exception is thrown at runtime.
  • 176. 2. Please place the correct Strings to the Output empty boxes.
  • 177. 3. Which class implements the NavigatableMap ? A. HashMap. B. SortedMap. C. TreeMap. D. LinkedHashMap. E. NavigateMap.
  • 179. What is the result? A. 2 B. 3 C. 4 D. 16 E. Compilation fails.
  • 180. 5. Given: What is the result? A. 3.14 50 100 B. 100 50 3.14 C. Compilation fails. D. An exception is thrown at line 4. E. An exception is thrown at line 5. F. An exception is thrown at line 7.
  • 181.
  • 182.  Place the correct Animals to the Outputs empty boxes. (Animals can be used more than once . )
  • 183. 7. Which two, inserted at line 1, will compile? (Choos two.) A. B. C. D. E. F. class MaxValue<X> { class MaxValue<X extends Object> { class MaxValue<X extends Number> { class MaxValue<X extends Integer> { class MaxValue<?> { class MaxValue<? extends Number> {
  • 184.
  • 185. 8. The following class is designed for the key value of java.util.HashMap. Which two method should be overridden to make the MyKey work correctly? (Choose two.) A. public String toString() B. public boolean equals(MyKey k) C. public int compareTo(MyKey k) D. public int hashCode() E. public boolean equals(Object k)
  • 186.
  • 187. 9. Given: Which code, inser ted a t line 4, will guarantee to output [4, 5]? A. Set x = new HashSet(); B. Set x = new SortedSet(); C. Set x = new TreeSet(); D. List x = new ArrayList(); E. List x = new LinkedList();