}
範例 11_泛型_04_限定的泛型 public class Ch04 {
public static void main(String args[]) {
//宣告與產生物件實體時,同時指定泛型的真實資料型態
MyClass<String,Integer> obj=new MyClass<String,Integer>();
System.out.println("在中間的是: "+obj.mid("哈囉","你好","!"));
System.out.println("在中間的是: "+obj.mid(1,2,3));
//System.out.println("在中間的是: "+obj.mid(1,2,"你好"));
} }
//宣告兩個泛型名稱,T1將來可以是任何的資料型態,但T2只能是Number類別的子孫類別型態,如Integer、Float等 class MyClass<T1,T2 extends Number>
{
T1 mid(T1 a, T1 b, T1 c) {
return b;
}
T2 mid(T2 a, T2 b, T2 c) {
return b;
} }
主題 12:集合 (Collection)
Collection 可以翻譯為集合,是將多個元素組織為一個單元的抽象設計方式。
Collection 有時也被稱為集合物件或容器,指一群相關聯的資料集合在一起組成一個物件。而集合物 件裡的資料,稱為元素。我們可以運用集合物件儲存、取用或操作資料(新增、刪除、排序等),或 將資料從一個方法傳遞到另一個方法。
至於要使用什麼樣的容器則依設計需求而定,可以使用循序有索引的 List 結構、不允許重複元素的 Set 結構、或是「鍵-值」(Key-Value)索引的 Map 結構來儲存資料。
Java Collections Framework 主要分為兩大繼承族譜,一類為 Collection 介面的子孫介面,另一類為 Map 介面的子孫介面。其中,Collection 介面一系代表的是單一元素系,而 Map 介面一系代表的是 Key-Value 的成對(pair)元素系。
首先介紹幾個主要介面的特性:
- Collection 介面
可以將此介面視為單一元素的集合,並未規定元素在集合內是否允許重複,也未規定是否具有順序,
進階的規定交由繼承的子介面來規範。
-Set 介面
其內不能有重複的元素,但元素的順序未加以規定,類似於數學上的集合。
-SortedSet 介面
繼承自 Set 介面,並且要求其內的元素會自動遞增排序,譬如英漢字典裡的單字排序就符合此介面 的規範。
-List 介面
其內可以有重複的元素,且元素在集合裡是有順序性的(物件加入容器的順序),每個元素都有其對 應的位置(數值的索引),可以透過位置來存取元素,譬如陣列(Array)就符合此介面的規範。
-Queue 介面
其內元素的取出具有一定的順序,即符合先進先出(first-in first-out)的原則。
-Map 介面
此介面為成對(pair)之集合,每個元素由兩個值組成,一個為 key 另一個為 value。此外,key 值是 不能重複的,且每個 key 只能映射(mapping)到一個 value,但每個 value 則不限定幾個 key 來對應。
每個元素必須同時有 key 與 value,成對表現才具意義。
-SortedMap 介面
繼承自 Map 介面,規定其內的元素必須按照 key 值自動排序。譬如成績單若依學號來排序,即符合 此介面的規範。
接下來介紹幾個常見的集合介面實作:
-HashSet 類別(Set 介面的實作)
HashSet 在實作時,採用資料結構中雜湊表(Hash Table)的方式來完成,元素的順序與存入時的順序 無關。HashSet 根據湊雜碼來確定元素於容器中儲存的位置,也根據雜湊碼來快速的找到容器中的元 素,在大多數情況下這樣的方式可以有效提升搜尋、新增、刪除元素的速度。
-TreeSet 類別(Set 介面或 SortedSet 的實作)
TreeSet 使用的是紅黑樹(Red-black tree)的資料結構來實作。每次有資料加進去,就會對資料作排序
(遞增)。
-ArrayList 類別(List 介面的實作)
ArrayList 使用陣列結構來實作,元素加入時是用索引值(index)依序儲存。陣列的特性是依據索引 來快速指定物件的位置,所以對於快速的隨機取得物件來說,使用 ArrayList 可以得到較好的效能,
但由於使用陣列實作,若要從中間作移除或插入物件的動作,會需要搬動後段的陣列元素以重新調整 索引順序,所以速度上就會慢的多。
-LinkedList 類別(List 介面與 Deque 介面的實作)
LinkedList 使用鏈結串列(Linked list)的資料結構來實作。鏈結串列是由許多節點(node)所構成的 串列(list),每個節點包含資料欄位與鏈結欄位,透過鏈結欄位指向下一個節點的位址,至於最後一 個節點的鏈結欄位則指向 null,代表串列已經結束。
鏈結串列在刪除或插入節點時,是透過改變鏈結欄位的方式達成,不必如同陣列般進行資料的大量搬 移,因此如果元素在加入之後大都是為了取出,而不會常作移除或插入的動作,建議使用 ArrayList 效 能上會比較好;但如果需要經常從容器中作移除或插入元素的動作,則使用 LinkedList 會獲得較好 的效能。
-HashMap 類別(Map 介面的實作)
HashMap 使用雜湊表結構來實作,儲存的元素分為 key 值與 value 值,形成一個 key-value pair,使 我們能依據 key 值快速查找到對應的資料。
-TreeMap 類別(Map 介面與 SortedMap 介面的實作)
TreeMap 使用紅黑樹結構來實作,儲存的元素同樣分為 key 值與 value 值,元素會依據 key 值由小 至大排序。
範例 12_集合_01_HashSet import java.util.HashSet;
public class Ch01 {
public static void main(String[] args) {
HashSet<String> color=new HashSet<String>(); //宣告一名為 color 的 HashSet 集合物件, 集合內元素 的型態為 String
String str1="green",str2="yellow";
System.out.println("color empty: "+color.isEmpty());
color.add("blue"); //新增元素 color.add("red");
color.add(str1);
color.add(str2);
System.out.println("新增重複元素: "+color.add("blue")); //若成功會回傳true System.out.println("color empty: "+color.isEmpty()); //集合是否為空
System.out.println("color size: "+color.size()); //集合大小 (元素數量) System.out.println("color 內容: "+color); //集合內容
color.remove(str1); //移除元素 System.out.println("color size: "+color.size());
System.out.println("color 內容: "+color);
System.out.println("color 中是否有 str1: "+color.contains(str1)); //是否包含元素 System.out.println("color 中是否有 blue: "+color.contains("blue"));
color.add("pink");
System.out.println("color 內容: "+color);
color.clear(); //清除集合中所有元素 System.out.println("color empty: "+color.isEmpty());
System.out.println("color 內容: "+color);
System.out.println("color size: "+color.size());
} }
範例 12_集合_02_TreeSet import java.util.TreeSet;
public class Ch02 {
public static void main(String[] args) {
TreeSet<Integer> n=new TreeSet<Integer>();
for(int i=20; i>=2; i-=2) n.add(i);
System.out.println("元素個數: "+n.size());
System.out.println("集合內容: "+n);
System.out.println("第一個元素: "+n.first());
System.out.println("最後一個元素: "+n.last());
System.out.println("介於6和14之間的集合: "+n.subSet(6, 14));
System.out.println("大於等於10的集合: "+n.tailSet(10));
System.out.println("小於8的集合: "+n.headSet(8));
} }
範例 12_集合_03_ArrayList import java.util.ArrayList;
public class Ch03 {
public static void main(String[] args) {
ArrayList<Integer> n=new ArrayList<Integer>(); //可將 ArrayList 集合視為可變更大小的動態陣列 for(int i=20; i>=2; i-=2)
n.add(i);
n.add(40);
n.add(60); //以上所加入的元素將依據加入的順序排列 n.add(0, 80); //指定索引值為 0 加入元素
n.add(1, 100);
System.out.println("元素個數: "+n.size());
System.out.println("集合內容: "+n);
n.add(1, 150); //插入點後的所有元素其索引值都會改變 System.out.println("集合內容: "+n);
n.set(2, 10); //將索引值 2 的元素以 10 取代 System.out.println("集合內容: "+n);
n.remove(3); //移除索引值為 3 的元素 System.out.println("集合內容: "+n);
System.out.println(n.indexOf(10)); //第一個元素值為 10 的索引值
System.out.println(n.lastIndexOf(10)); //最後一個元素值為 10 的索引值 (由後往前搜尋第一個)
System.out.println(n.get(1)); //集合中索引值為 1 的元素 }
}
範例 12_集合_04_LinkedList import java.util.LinkedList;
public class Ch04 {
public static void main(String[] args) {
//LinedList 和 ArrayList 其實很像,多了處理 LinkedList<Integer> n=new LinkedList<Integer>();
for(int i=20; i>=2; i-=2) //元素將以加入的順序排列 n.add(i);
n.addFirst(10); //加入一個元素在鏈結串列的最前方 n.addLast(10); //加入一個元素在鏈結串列的最後方 n.addFirst(100);
System.out.println("元素個數: "+n.size());
System.out.println("集合內容: "+n);
n.removeFirstOccurrence(10); //移除第一個值為 10 的元素 n.removeLastOccurrence(10); //移除最後一個值為 10 的元素 System.out.println("集合內容: "+n);
n.removeFirst(); //移除鏈結串列中最前方的元素
n.removeLast(); //移除鏈結串列中最後方的元素 System.out.println("集合內容: "+n);
System.out.println(n.get(1)); //鏈結串列中索引值為 1 的元素 System.out.println(n.getFirst()); //鏈結串列中最前方的元素
System.out.println(n.getLast()); //鏈結串列中最後方的元素
System.out.println(n.indexOf(18)); //第一個元素值為 18 的索引值
System.out.println(n.lastIndexOf(18)); //最後一個元素值為 18 的索引值 (由後往前搜尋第一個)
System.out.println(n.indexOf(30)); //若回傳 -1 代表找不到該元素 }
}
範例 12_集合_05_HashMap import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
public class Ch05 {
public static void main(String[] args) {
HashMap<Integer,String> hm=new HashMap<Integer,String>();
hm.put(92001, "Ben");
hm.put(92002, "Peter");
hm.put(92003, "Tony");
System.out.println("HashMap 是否為空: "+hm.isEmpty());
System.out.println("元素個數: "+hm.size());
System.out.println("HashMap 的內容: "+hm);
System.out.println("HashMap 中是否有關鍵值為 92001 的元素: "+hm.containsKey(92001));
System.out.println("HashMap 中是否有對應值為 Kevin 的元素: "+hm.containsValue("Kevin"));
hm.put(92001, "John"); //覆蓋舊值
System.out.println("HashMap 的內容: "+hm);
hm.remove(92002); //移除關鍵值為 92002 的元素 System.out.println("HashMap 的內容: "+hm);
System.out.println("關鍵值 92003 的對應值: "+hm.get(92003));
Set<Integer> id=hm.keySet(); //將各元素的 K 欄位集合轉換為 Set 物件 System.out.println("ID 集合內容為: "+id);
Collection<String> name=hm.values(); //將各元素的 V 欄位集合轉換為 Collection 物件 System.out.println("姓名集合內容為: "+name);
} }
範例 12_集合_06_TreeMap import java.util.TreeMap;
public class Ch06 {
public static void main(String[] args) {
TreeMap<Integer,String> tm=new TreeMap<Integer,String>();
tm.put(92001, "Ben");
tm.put(92002, "Peter");
tm.put(92003, "Tony");
System.out.println("TreeMap 是否為空: "+tm.isEmpty());
System.out.println("元素個數: "+tm.size());
System.out.println("TreeMap 的內容: "+tm); //依 key 值遞增排序
System.out.println("TreeMap 中是否有關鍵值為 92001 的元素: "+tm.containsKey(92001));
System.out.println("TreeMap 中是否有對應值為 Kevin 的元素: "+tm.containsValue("Kevin"));
System.out.println("第一個元素為: "+tm.firstKey()+" "+tm.get(tm.firstKey()));
System.out.println("最後一個元素為: "+tm.lastKey()+" "+tm.get(tm.lastKey()));
System.out.println("關鍵值大於或等於 92002 的元素: "+tm.tailMap(92002));
System.out.println("關鍵值大於或等於 92002 且小於 92003 的元素: "+tm.subMap(92002,92003));
} }
再接著我們介紹 Iterator 與 ListIterator 介面:
Iterator 與 ListIterator 介面,可用來「走訪」或是刪除集合物件的元素。Iterator 物件的讀取是單向的,
且只能讀取一次;而 ListIterator 物件的走訪可以是雙向的(正向、反向)。
範例 12_集合_07_Iterator 介面 import java.util.TreeSet;
import java.util.Iterator;
public class Ch07 {
public static void main(String[] args) {
TreeSet<Integer> ts=new TreeSet<Integer>();
for(int i=20; i>=2; i-=2) ts.add(i);
System.out.println("元素個數: "+ts.size());
System.out.println("集合內容: "+ts);
Iterator<Integer> itr=ts.iterator(); //將 TreeSet 集合物件轉換為 Iterator 物件 System.out.print("正向走訪: "); //只能讀取一次
while(itr.hasNext()) //若仍有下個元素則回傳 true System.out.print(itr.next()+" "); //回傳下個元素 System.out.println();
itr.remove(); //移除最後讀取的元素 System.out.println("集合內容: "+ts);
} }
範例 12_集合_08_ListIterator 介面 import java.util.LinkedList;
import java.util.ListIterator;
public class Ch08 {
public static void main(String[] args) {
LinkedList<Integer> ts=new LinkedList<Integer>();
for(int i=20; i>=2; i-=2) ts.add(i);
System.out.println("元素個數: "+ts.size());
System.out.println("集合內容: "+ts);
ListIterator<Integer> itr1=ts.listIterator(); //將 LinkedList 集合物件轉換為 ListIterator 物件
ListIterator<Integer> itr2=ts.listIterator(ts.size()); //將 LinkedList 集合物件轉換為 ListIterator 物件, 並指 定指標位置(介於元素之間)
System.out.print("正向走訪: "); //只能讀取一次 while(itr1.hasNext()) //若仍有下個元素則回傳 true
System.out.print(itr1.next()+" "); //回傳下個元素 System.out.println();
System.out.print("反向走訪: "); //同樣只能讀取一次 while(itr2.hasPrevious()) //若仍有下個元素則回傳 true
System.out.print(itr2.previous()+" "); //回傳下個元素 System.out.println();
itr2.remove(); //移除最後讀取的元素 System.out.println("集合內容: "+ts);
} }
最後介紹 Collections 類別:
Collections 類別是 Java Collections Framework 中專門負責演算法的類別。該類別中提供了多個有用的 static 方法,較常用的有 binarySearch()、copy()、max()、min()、reverse()、sort()、swap() 等。
範例 12_集合_09_Collections 類別 import java.util.Collections;
import java.util.HashSet;
public class Ch09 {
public static void main(String[] args) {
HashSet<Integer> hs=new HashSet<Integer>();
hs.add(11);
hs.add(8);
hs.add(2);
hs.add(33);
hs.add(29);
System.out.println("集合內容: "+hs);
System.out.println("集合中最大的數: "+Collections.max(hs));
System.out.println("集合中最小的數: "+Collections.min(hs));
//Collections.sort(hs); //包含 sort(),reverse() 等多數 Collections 類別所提供的方法只適用 List 介面 的實作
} }
範例 12_集合_10_其他應用 1 import java.util.HashSet;
import java.util.TreeSet;
import java.util.LinkedList;
import java.util.Collections;
public class Ch10 {
public static void main(String[] args) {
HashSet<Integer> hs=new HashSet<Integer>();
//利用 HashSet 元素不得重複的特性, 產生不重複之隨機亂數 while(hs.size()<6) //當元素數量達到 6 時, 停止加入元素 {
//System.out.println(hs.add((int)(Math.random()*39+1)));
//若出現重複則會造成元素加入失敗, 並回傳 false hs.add((int)(Math.random()*39+1)); //1~39
}
System.out.println("本期樂透號碼: "+hs);
TreeSet<Integer> ts=new TreeSet<Integer>(hs); //將 HashSet 集合物件, 轉換為 TreeSet 集合物件 //TreeSet<Integer> ts=new TreeSet<Integer>();
//ts.addAll(hs);
System.out.println("本期樂透號碼: "+ts);
LinkedList<Integer> ll=new LinkedList<Integer>(hs); //將 HashSet 集合物件, 轉換為 LinkedList 集合物 件
System.out.println("本期樂透號碼: "+ll);
Collections.sort(ll);
System.out.println("本期樂透號碼: "+ll);
Collections.reverse(ll);
System.out.println("本期樂透號碼: "+ll);
} }
範例 12_集合_11_其他應用 2 //import java.util.Collections;
import java.util.LinkedList;
import java.util.ListIterator;
public class Ch11 {
public static void main(String[] args) { Stu stu1=new Stu(92001,"Tony",95);
Stu stu2=new Stu(92002,"Ben",80);
Stu stu3=new Stu(92003,"Peter",85);
Stu stu4=new Stu(92004,"May",75);
LinkedList<Stu> ll=new LinkedList<Stu>(); //元素為 Stu 物件 ll.add(stu1);
ll.add(stu2);
ll.add(stu3);
ll.add(stu4);
//System.out.println("集合內容: "+ll); //無法顯示 System.out.println("使用 for 迴圈正向走訪:");
for(Stu s:ll)
s.showProfile();
System.out.println();
ListIterator<Stu> itr=ll.listIterator();
System.out.println("使用 ListIterator 介面正向走訪:");
while(itr.hasNext()) {
itr.next().showProfile(); //itr.next() 所取出的元素為物件 }
//Collections.sort(ll); //集合內容為物件, 無法排序 }
} class Stu {
int id,score;
String name;
Stu(int id, String name, int score) {
this.id=id;
this.name=name;
this.score=score;
}
void showProfile() {
System.out.println("學號: "+id+"\t姓名: "+name+"\t成績: "+score);
} }
範例 12_集合_12_其他應用 3 import java.util.HashSet;
import java.util.HashMap;
public class Ch12 {
public static void main(String[] args) { int n[]={23,45,73,22,45};
for(int i:n) //for 迴圈針對 Collection 或 Array 的特殊寫法 System.out.print(i+" ");
System.out.println();
System.out.println();