Java Collection

Yake1965 2022-01-07 20:20:45 阅读数:263

java collection

Java Collection

集合類也稱為容器類。Java 所有的集合類都比特於 java.util 包下,提供了一個錶示和操作對象集合的統一構架,包含大量集合接口,以及這些接口的實現類和操作它們的算法。

集合類和數組不一樣,數組元素既可以是基本類型的值,也可以是對象,而集合裏只能保存對象。

Java 集合類型分為 Collection 和 Map,它們是 Java 集合的根接口,這兩個接口又包含了一些子接口或實現類。圖 1 和圖 2 分別為 Collection 和 Map 的子接口及其實現類。

在這裏插入圖片描述

Iterator 接口 集合的輸出接口,主要用於遍曆輸出(即迭代訪問)Collection 集合中的元素,Iterator 對象被稱之為迭代器。迭代器接口是集合接口的父接口,實現類實現 Collection 時就必須實現 Iterator 接口。
Collection 接口 是 List、Set 和 Queue 的父接口,是存放一組單值的最大接口。所謂的單值是指集合中的每個元素都是一個對象。一般很少直接使用此接口直接操作。
Queue 接口 Queue 是 Java 提供的隊列實現,有點類似於 List。
Dueue 接口 是 Queue 的一個子接口,為雙向隊列。
List 接口 是最常用的接口。是有序集合,允許有相同的元素。使用 List 能够精確地控制每個元素插入的比特置,用戶能够使用索引(元素在 List 中的比特置,類似於數組下標)來訪問 List 中的元素,與數組類似。
Set 接口 不能包含重複的元素。
Map 接口 是存放一對值的最大接口,即接口中的每個元素都是一對,以 keyvalue 的形式保存。

對於 Set、List、Queue 和 Map 這 4 種集合,Java 最常用的實現類分別是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。

HashSet 為優化査詢速度而設計的 Set。它是基於 HashMap 實現的,HashSet 底層使用 HashMap 來保存所有元素,實現比較簡單
TreeSet 實現了 Set 接口,是一個有序的 Set,這樣就能從 Set 裏面提取一個有序序列
ArrayList 一個用數組實現的 List,能進行快速的隨機訪問,效率高而且實現了可變大小的數組
ArrayDueue 是一個基於數組實現的雙端隊列,按“先進先出”的方式操作集合元素
LinkedList 對順序訪問進行了優化,但隨機訪問的速度相對較慢。此外它還有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它當成棧(Stack)或隊列(Queue)來用
HsahMap 按哈希算法來存取鍵對象
TreeMap 可以對鍵對象進行排序

Java Collection 接口

Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定義了一些通用的方法,通過這些方法可以實現對集合的基本操作。定義的方法既可用於操作 Set 集合,也可用於操作 List 和 Queue 集合。

Collection 接口中常用的方法:

boolean add(E e) 向集合中添加一個元素,如果集合對象被添加操作改變了,則返回 true。E 是元素的數據類型
boolean addAll(Collection c) 向集合中添加集合 c 中的所有元素,如果集合對象被添加操作改變了,則返回 true。
void clear() 清除集合中的所有元素,將集合長度變為 0。
boolean contains(Object o) 判斷集合中是否存在指定元素
boolean containsAll(Collection c) 判斷集合中是否包含集合 c 中的所有元素
boolean isEmpty() 判斷集合是否為空
Iteratoriterator() 返回一個 Iterator 對象,用於遍曆集合中的元素
boolean remove(Object o) 從集合中删除一個指定元素,當集合中包含了一個或多個元素 o 時,該方法只删除第一個符合條件的元素,該方法將返回 true。
boolean removeAll(Collection c) 從集合中删除所有在集合 c 中出現的元素(相當於把調用該方法的集合减去集合 c)。如果該操作改變了調用該方法的集合,則該方法返回 true。
boolean retainAll(Collection c) 從集合中删除集合 c 裏不包含的元素(相當於把調用該方法的集合變成該集合和集合 c 的交集),如果該操作改變了調用該方法的集合,則該方法返回 true。
int size() 返回集合中元素的個數
Object[] toArray() 把集合轉換為一個數組,所有的集合元素變成對應的數組元素。
ArrayList list1 = new ArrayList(); // 創建集合 list1
ArrayList list2 = new ArrayList(); // 創建集合 list2
list1.add("one"); // 向 list1 添加一個元素
list1.add("two"); // 向 list1 添加一個元素
list2.addAll(list1); // 將 list1 的所有元素添加到 list2
list2.add("three"); // 向 list2 添加一個元素
System.out.println("list2 集合中的元素如下:");
Iterator it1 = list2.iterator();
while (it1.hasNext()) {

System.out.print(it1.next() + "、");
}

由於 Collection 是接口,不能對其實例化,所以上述代碼中使用了 Collection 接口的 ArrayList 實現類來調用 Collection 的方法。add() 方法可以向 Collection 中添加一個元素,而調用 addAll() 方法可以將指定 Collection 中的所有元素添加到另一個 Collection 中。

ArrayList list1 = new ArrayList(); // 創建集合 list1
ArrayList list2 = new ArrayList(); // 創建集合 list2
list1.add("one");
list1.add("two");
list1.add("three");
System.out.println("list1 集合中的元素數量:" + list1.size()); // 輸出list1中的元素數量
list2.add("two");
list2.add("four");
list2.add("six");
System.out.println("list2 集合中的元素數量:" + list2.size()); // 輸出list2中的元素數量
list2.remove(2); // 删除第 3 個元素
System.out.println("\nremoveAll() 方法之後 list2 集合中的元素數量:" + list2.size());
System.out.println("list2 集合中的元素如下:");
Iterator it1 = list2.iterator();
while (it1.hasNext()) {

System.out.print(it1.next() + "、");
}
list1.removeAll(list2);
System.out.println("\nremoveAll() 方法之後 list1 集合中的元素數量:" + list1.size());
System.out.println("list1 集合中的元素如下:");
Iterator it2 = list1.iterator();
while (it2.hasNext()) {

System.out.print(it2.next() + "、");
}

list2 集合在調用 remove(2) 方法删除第 3 個元素之後剩下了 two 和 four。list1.removeAll(list2) 語句會從 list1 中將 list1 和 list2 中相同的元素删除,即删除 two 元素。最後輸出結果如下:

list1 集合中的元素數量:3
list2 集合中的元素數量:3

removeAll() 方法之後 list2 集合中的元素數量:2
list2 集合中的元素如下:
two、four、
removeAll() 方法之後 list1 集合中的元素數量:2
list1 集合中的元素如下:
one、three、

注意:retainAll( ) 方法的作用與 removeAll( ) 方法相反,即保留兩個集合中相同的元素,其他全部删除。

編譯上面程序時,系統可能輸出一些警告提示,這些警告是提示用戶沒有使用泛型來限制集合裏的元素類型。

在傳統模式下,把一個對象“丟進”集合中後,集合會忘記這個對象的類型。也就是說,系統把所有的集合元素都當成 Object 類型。從 Java 5 以後,可以使用泛型來限制集合裏元素的類型,並讓集合記住所有集合元素的類型。

Java List集合:ArrayList 和 LinkedList 類的用法及區別

List 是一個有序、可重複的集合,集合中每個元素都有其對應的順序索引。List 集合允許使用重複元素,可以通過索引來訪問指定比特置的集合元素。List 集合默認按元素的添加順序設置元素的索引,第一個添加到 List 集合中的元素的索引為 0,第二個為 1,依此類推。

List 實現了 Collection 接口,它主要有兩個常用的實現類:ArrayList 類和 LinkedList 類。

ArrayList 類

ArrayList 類實現了可變數組的大小,存儲在內的數據稱為元素。它還提供了快速基於索引訪問元素的方式,對尾部成員的增加和删除支持較好。使用 ArrayList 創建的集合,允許對集合中的元素進行快速的隨機訪問,不過,向 ArrayList 中插入與删除元素的速度相對較慢。

ArrayList 類的常用構造方法有如下兩種重載形式:

ArrayList():構造一個初始容量為 10 的空列錶。
ArrayList(Collection<?extends E>c):構造一個包含指定 Collection 元素的列錶,這些元素是按照該 Collection 的迭代器返回它們的順序排列的。
ArrayList 類除了包含 Collection 接口中的所有方法之外,還包括 List 接口中提供的如錶 1 所示的方法。

E get(int index) 獲取此集合中指定索引比特置的元素,E 為集合中元素的數據類型
int index(Object o) 返回此集合中第一次出現指定元素的索引,如果此集合不包含該元 素,則返回 -1
int lastIndexOf(Object o) 返回此集合中最後一次出現指定元素的索引,如果此集合不包含該 元素,則返回 -1
E set(int index, Eelement) 將此集合中指定索引比特置的元素修改為 element 參數指定的對象。 此方法返回此集合中指定索引比特置的原元素
List subList(int fromlndex, int tolndex) 返回一個新的集合,新集合中包含 fromlndex 和 tolndex 索引之間 的所有元素。包含 fromlndex 處的元素,不包含 tolndex 索引處的 元素

注意:當調用 List 的 set(int index, Object element) 方法來改變 List 集合指定索引處的元素時,指定的索引必須是 List 集合的有效索引。例如集合長度為 4,就不能指定替換索引為 4 處的元素,也就是說這個方法不會改變 List 集合的長度。

例 1
使用 ArrayList 類向集合中添加三個商品信息,包括商品編號、名稱和價格,然後遍曆集合輸出這些商品信息。

1)創建一個商品類 Product,在該類中定義 3 個屬性和 toString() 方法,分別實現 setter/getter 方法。代碼的實現如下:

public class Product {

// 商品類
private int id; // 商品編號
private String name; // 名稱
private float price; // 價格
public Product(int id, String name, float price) {

this.name = name;
this.id = id;
this.price = price;
}
// 這裏是上面3個屬性的setter/getter方法,這裏省略
public String toString() {

return "商品編號:" + id + ",名稱:" + name + ",價格:" + price;
}
}

2)創建一個測試類,調用 Product 類的構造函數實例化三個對象,並將 Product 對象保存至 ArrayList 集合中。最後遍曆該集合,輸出商品信息。測試類的代碼實現如下:

public class Test {

public static void main(String[] args) {

Product pd1 = new Product(4, "木糖醇", 10);
Product pd2 = new Product(5, "洗發水", 12);
Product pd3 = new Product(3, "熱水壺", 49);
List list = new ArrayList(); // 創建集合
list.add(pd1);
list.add(pd2);
list.add(pd3);
System.out.println("*************** 商品信息 ***************");
for (int i = 0; i < list.size(); i++) {

// 循環遍曆集合,輸出集合元素
Product product = (Product) list.get(i);
System.out.println(product);
}
}
}

該示例中的 ArrayList 集合中存放的是自定義類 Product 的對象,這與存儲的 String 類的對象是相同的。與 Set 不同的是,List 集合中存在 get() 方法,該方法可以通過索引來獲取所對應的值,獲取的值為 Object 類,因此需要將該值轉換為 Product 類,從而獲取商品信息。

例 2
在使用 List 集合時需要注意區分 indexOf() 方法和 lastIndexOf() 方法。前者是獲得指定對象的最小索引比特置,而後者是獲得指定對象的最大索引比特置。前提條件是指定的對象在 List 集合中有重複的對象,否則這兩個方法獲取的索引值相同。

下面的案例代碼演示了 indexOf() 方法和 lastIndexOf() 方法的區別。

public static void main(String[] args) {

List list = new ArrayList();
list.add("One");
list.add("|");
list.add("Two");
list.add("|");
list.add("Three");
list.add("|");
list.add("Four");
System.out.println("list 集合中的元素數量:" + list.size());
System.out.println("list 集合中的元素如下:");
Iterator it = list.iterator();
while (it.hasNext()) {

System.out.print(it.next() + "、");
}
System.out.println("\n在 list 集合中'丨'第一次出現的比特置是:" + list.indexOf("|"));
System.out.println("在 list 集合中'丨'最後一次出現的比特置是:" + list.lastIndexOf("|"));
}

上述代碼創建一個 List 集合 list,然後添加了 7 個元素,由於索引從 0 開始,所以最後一個元素的索引為 6。

例 3
使用 subList() 方法截取 List 集合中部分元素時要注意,新的集合中包含起始索引比特置的元素,但是不包含結束索引比特置的元素。例如,subList(1,4) 方法實際截取的是索引 1 到索引 3 的元素,並組成新的 List 集合。

下面的案例代碼演示了 subList() 方法的具體用法。

public static void main(String[] args) {

List list = new ArrayList();
list.add("One");
list.add("Two");
list.add("Three");
list.add("Four");
list.add("Five");
list.add("Six");
list.add("Seven");
System.out.println("list 集合中的元素數量:" + list.size());
System.out.println("list 集合中的元素如下:");
Iterator it = list.iterator();
while (it.hasNext()) {

System.out.print(it.next() + "、");
}
List sublist = new ArrayList();
sublist = list.subList(2, 5); // 從list集合中截取索引2~5的元素,保存到sublist集合中
System.out.println("\nsublist 集合中元素數量:" + sublist.size());
System.out.println("sublist 集合中的元素如下:");
it = sublist.iterator();
while (it.hasNext()) {

System.out.print(it.next() + "、");
}
}

LinkedList類

LinkedList 類采用鏈錶結構保存對象,這種結構的優點是便於向集合中插入或者删除元素。需要頻繁向集合中插入和删除元素時,使用 LinkedList 類比 ArrayList 類效果高,但是 LinkedList 類隨機訪問元素的速度則相對較慢。這裏的隨機訪問是指檢索集合中特定索引比特置的元素。

LinkedList 類除了包含 Collection 接口和 List 接口中的所有方法之外,還特別提供了錶 2 所示的方法。

void addFirst(E e) 將指定元素添加到此集合的開頭
void addLast(E e) 將指定元素添加到此集合的末尾
E getFirst() 返回此集合的第一個元素
E getLast() 返回此集合的最後一個元素
E removeFirst() 删除此集合中的第一個元素
E removeLast() 删除此集合中的最後一個元素

例 4
在倉庫管理系統中要記錄入庫的商品名稱,並且需要輸出第一個錄入的商品名稱和最後—個商品名稱。下面使用 LinkedList 集合來完成這些功能,實現代碼如下:

public class Test {

public static void main(String[] args) {

LinkedList<String> products = new LinkedList<String>(); // 創建集合對象
String p1 = new String("六角螺母");
String p2 = new String("10A 電纜線");
String p3 = new String("5M 卷尺");
String p4 = new String("4CM 原木方板");
products.add(p1); // 將 p1 對象添加到 LinkedList 集合中
products.add(p2); // 將 p2 對象添加到 LinkedList 集合中
products.add(p3); // 將 p3 對象添加到 LinkedList 集合中
products.add(p4); // 將 p4 對象添加到 LinkedList 集合中
String p5 = new String("標准文件夾小櫃");
products.addLast(p5); // 向集合的末尾添加p5對象
System.out.print("*************** 商品信息 ***************");
System.out.println("\n目前商品有:");
for (int i = 0; i < products.size(); i++) {

System.out.print(products.get(i) + "\t");
}
System.out.println("\n第一個商品的名稱為:" + products.getFirst());
System.out.println("最後一個商品的名稱為:" + products.getLast());
products.removeLast(); // 删除最後一個元素
System.out.println("删除最後的元素,目前商品有:");
for (int i = 0; i < products.size(); i++) {

System.out.print(products.get(i) + "\t");
}
}
}

如上述代碼,首先創建了 5 個 String 對象,分別為 p1、p2、p3、p4 和 p5。同時將 p1、 p2、p3 和 p4 對象使用 add() 方法添加到 LinkedList 集合中,使用 addLast() 方法將 p5 對象添加到 LinkedList 集合中。分別調用 LinkedList 類中的 getFirst() 方法和 getLast() 方法獲取第一個和最後一個商品名稱。最後使用 removeLast() 方法將最後一個商品信息删除,並將剩餘商品信息打印出來。

LinkedList 中的 是 Java 中的泛型,用於指定集合中元素的數據類型,例如這裏指定元素類型為 String,則該集合中不能添加非 String 類型的元素。

ArrayList 類和 LinkedList 類的區別

ArrayList 與 LinkedList 都是 List 接口的實現類,因此都實現了 List 的所有未實現的方法,只是實現的方式有所不同。

ArrayList 是基於動態數組數據結構的實現,訪問元素速度優於 LinkedList。LinkedList 是基於鏈錶數據結構的實現,占用的內存空間比較大,但在批量插入或删除數據時優於 ArrayList。

對於快速訪問對象的需求,使用 ArrayList 實現執行效率上會比較好。需要頻繁向集合中插入和删除元素時,使用 LinkedList 類比 ArrayList 類效果高。

不同的結構對應於不同的算法,有的考慮節省占用空間,有的考慮提高運行效率,對於程序員而言,它們就像是“熊掌”和“魚肉”,不可兼得。高運行速度往往是以犧牲空間為代價的,而節省占用空間往往是以犧牲運行速度為代價的。

Java Set 集合:HashSet 和 TreeSet 類

Set 集合類似於一個罐子,程序可以依次把多個對象“丟進”Set 集合,而 Set 集合通常不能記住元素的添加順序。也就是說 Set 集合中的對象不按特定的方式排序,只是簡單地把對象加入集合。Set 集合中不能包含重複的對象,並且最多只允許包含一個 null 元素。

Set 實現了 Collection 接口,它主要有兩個常用的實現類:HashSet 類和 TreeSet類。

HashSet 類

HashSet 是 Set 接口的典型實現,大多數時候使用 Set 集合時就是使用這個實現類。HashSet 是按照 Hash 算法來存儲集合中的元素。因此具有很好的存取和查找性能。

HashSet 具有以下特點:

不能保證元素的排列順序,順序可能與添加順序不同,順序也有可能發生變化。
HashSet 不是同步的,如果多個線程同時訪問或修改一個 HashSet,則必須通過代碼來保證其同步。
集合元素值可以是 null。
當向 HashSet 集合中存入一個元素時,HashSet 會調用該對象的 hashCode() 方法來得到該對象的 hashCode 值,然後根據該 hashCode 值决定該對象在 HashSet 中的存儲比特置。如果有兩個元素通過 equals() 方法比較返回的結果為 true,但它們的 hashCode 不相等,HashSet 將會把它們存儲在不同的比特置,依然可以添加成功。

也就是說,兩個對象的 hashCode 值相等且通過 equals() 方法比較返回結果為 true,則 HashSet 集合認為兩個元素相等。

在 HashSet 類中實現了 Collection 接口中的所有方法。HashSet 類的常用構造方法重載形式如下。

HashSet():構造一個新的空的 Set 集合。
HashSet(Collection<? extends E>c):構造一個包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 錶示 HashSet 的父類,即指明該 Set 集合中存放的集合元素類型。c 錶示其中的元素將被存放在此 Set 集合中。
下面的代碼演示了創建兩種不同形式的 HashSet 對象。

HashSet hs = new HashSet(); // 調用無參的構造函數創建HashSet對象
HashSet hss = new HashSet(); // 創建泛型的 HashSet 集合對象

例 1
編寫一個 Java 程序,使用 HashSet 創建一個 Set 集合,並向該集合中添加 4 套教程。具體實現代碼如下:

public static void main(String[] args) {

HashSet<String> courseSet = new HashSet<String>(); // 創建一個空的 Set 集合
String course1 = new String("Java入門教程");
String course2 = new String("Python基礎教程");
String course3 = new String("C語言學習教程");
String course4 = new String("Golang入門教程");
courseSet.add(course1); // 將 course1 存儲到 Set 集合中
courseSet.add(course2); // 將 course2 存儲到 Set 集合中
courseSet.add(course3); // 將 course3 存儲到 Set 集合中
courseSet.add(course4); // 將 course4 存儲到 Set 集合中
System.out.println("C語言中文網教程有:");
Iterator<String> it = courseSet.iterator();
while (it.hasNext()) {

System.out.println("《" + (String) it.next() + "》"); // 輸出 Set 集合中的元素
}
System.out.println("有" + courseSet.size() + "套精彩教程!");
}

如上述代碼,首先使用 HashSet 類的構造方法創建了一個 Set 集合,接著創建了 4 個 String 類型的對象,並將這些對象存儲到 Set 集合中。使用 HashSet 類中的 iterator() 方法獲取一個 Iterator 對象,並調用其 hasNext() 方法遍曆集合元素,再將使用 next() 方法讀取的元素强制轉換為 String 類型。最後調用 HashSet 類中的 size() 方法獲取集合元素個數。

注意:在以上示例中,如果再向 CourseSet 集合中再添加一個名稱為“Java入門教程”的 String 對象,則輸出的結果與上述執行結果相同。也就是說,如果向 Set 集合中添加兩個相同的元素,則後添加的會覆蓋前面添加的元素,即在 Set 集合中不會出現相同的元素。

TreeSet 類

TreeSet 類同時實現了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以實現對集合進行自然排序,因此使用 TreeSet 類實現的 Set 接口默認情况下是自然排序的,這裏的自然排序指的是昇序排序。

TreeSet 只能對實現了 Comparable 接口的類對象進行排序,因為 Comparable 接口中有一個 compareTo(Object o) 方法用於比較兩個對象的大小。例如 a.compareTo(b),如果 a 和 b 相等,則該方法返回 0;如果 a 大於 b,則該方法返回大於 0 的值;如果 a 小於 b,則該方法返回小於 0 的值。

錶 1 列舉了 JDK 類庫中實現 Comparable 接口的類,以及這些類對象的比較方式。

包裝類(BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short) 按數字大小比較

Character 按字符的 Unicode 值的數字大小比較
String 按字符串中字符的 Unicode 值的數字大小比較
TreeSet 類除了實現 Collection 接口的所有方法之外,還提供了如錶 2 所示的方法。
E first() 返回此集合中的第一個元素。其中,E 錶示集合中元素的數據類型
E last() 返回此集合中的最後一個元素
E poolFirst() 獲取並移除此集合中的第一個元素
E poolLast() 獲取並移除此集合中的最後一個元素
SortedSet subSet(E fromElement,E toElement) 返回一個新的集合,新集合包含原集合中 fromElement 對象與 toElement 對象之間的所有對象。包含 fromElement 對象,不包含 toElement 對象
SortedSet headSet<E toElement〉 返回一個新的集合,新集合包含原集合中 toElement 對象之前的所有對象。 不包含 toElement 對象
SortedSet tailSet(E fromElement) 返回一個新的集合,新集合包含原集合中 fromElement 對象之後的所有對 象。包含 fromElement 對象

注意:錶面上看起來這些方法很多,其實很簡單。因為 TreeSet 中的元素是有序的,所以增加了訪問第一個、前一個、後一個、最後一個元素的方法,並提供了 3 個從 TreeSet 中截取子 TreeSet 的方法。

例 2
本次有 5 名學生參加考試,當老師錄入每名學生的成績後,程序將按照從低到高的排列順序顯示學生成績。此外,老師可以查詢本次考試是否有滿分的學生存在,不及格的成績有哪些,90 分以上成績的學生有幾名。

下面使用 TreeSet 類來創建 Set 集合,完成學生成績查詢功能。具體的代碼如下:

public class Test08 {

public static void main(String[] args) {

TreeSet<Double> scores = new TreeSet<Double>(); // 創建 TreeSet 集合
Scanner input = new Scanner(System.in);
System.out.println("------------學生成績管理系統-------------");
for (int i = 0; i < 5; i++) {

System.out.println("第" + (i + 1) + "個學生成績:");
double score = input.nextDouble();
// 將學生成績轉換為Double類型,添加到TreeSet集合中
scores.add(Double.valueOf(score));
}
Iterator<Double> it = scores.iterator(); // 創建 Iterator 對象
System.out.println("學生成績從低到高的排序為:");
while (it.hasNext()) {

System.out.print(it.next() + "\t");
}
System.out.println("\n請輸入要查詢的成績:");
double searchScore = input.nextDouble();
if (scores.contains(searchScore)) {

System.out.println("成績為: " + searchScore + " 的學生存在!");
} else {

System.out.println("成績為: " + searchScore + " 的學生不存在!");
}
// 查詢不及格的學生成績
SortedSet<Double> score1 = scores.headSet(60.0);
System.out.println("\n不及格的成績有:");
for (int i = 0; i < score1.toArray().length; i++) {

System.out.print(score1.toArray()[i] + "\t");
}
// 查詢90分以上的學生成績
SortedSet<Double> score2 = scores.tailSet(90.0);
System.out.println("\n90 分以上的成績有:");
for (int i = 0; i < score2.toArray().length; i++) {

System.out.print(score2.toArray()[i] + "\t");
}
}
}

如上述代碼,首先創建一個 TreeSet 集合對象 scores,並向該集合中添加 5 個 Double 對象。接著使用 while 循環遍曆 scores 集合對象,輸出該對象中的元素,然後調用 TreeSet 類中的 contains() 方法獲取該集合中是否存在指定的元素。最後分別調用 TreeSet 類中的 headSet() 方法和 tailSet() 方法獲取不及格的成績和 90 分以上的成績。

注意:在使用自然排序時只能向 TreeSet 集合中添加相同數據類型的對象,否則會拋出 ClassCastException 异常。如果向 TreeSet 集合中添加了一個 Double 類型的對象,則後面只能添加 Double 對象,不能再添加其他類型的對象,例如 String 對象等。

Java Map

Map 是一種鍵-值對(key-value)集合,Map 集合中的每一個元素都包含一個鍵(key)對象和一個值(value)對象。用於保存具有映射關系的數據。

Map 集合裏保存著兩組值,一組值用於保存 Map 裏的 key,另外一組值用於保存 Map 裏的 value,key 和 value 都可以是任何引用類型的數據。Map 的 key 不允許重複,value 可以重複,即同一個 Map 對象的任何兩個 key 通過 equals 方法比較總是返回 false。

Map 中的 key 和 value 之間存在單向一對一關系,即通過指定的 key,總能找到唯一的、確定的 value。從 Map 中取出數據時,只要給出指定的 key,就可以取出對應的 value。

Map 接口主要有兩個實現類:HashMap 類和 TreeMap 類。其中,HashMap 類按哈希算法來存取鍵對象,而 TreeMap 類可以對鍵對象進行排序。

Map 接口中提供的常用方法如錶 1 所示。

void clear() 删除該 Map 對象中的所有 key-value 對。
boolean containsKey(Object key) 查詢 Map 中是否包含指定的 key,如果包含則返回 true。
boolean containsValue(Object value) 查詢 Map 中是否包含一個或多個 value,如果包含則返回 true。
V get(Object key) 返回 Map 集合中指定鍵對象所對應的值。V 錶示值的數據類型
V put(K key, V value) 向 Map 集合中添加鍵-值對,如果當前 Map 中已有一個與該 key 相等的 key-value 對,則新的 key-value 對會覆蓋原來的 key-value 對。
void putAll(Map m) 將指定 Map 中的 key-value 對複制到本 Map 中。
V remove(Object key) 從 Map 集合中删除 key 對應的鍵-值對,返回 key 對應的 value,如果該 key 不存在,則返回 null
boolean remove(Object key, Object value) 這是 Java 8 新增的方法,删除指定 key、value 所對應的 key-value 對。如果從該 Map 中成功地删除該 key-value 對,該方法返回 true,否則返回 false。
Set entrySet() 返回 Map 集合中所有鍵-值對的 Set 集合,此 Set 集合中元素的數據類型為 Map.Entry
Set keySet() 返回 Map 集合中所有鍵對象的 Set 集合
boolean isEmpty() 查詢該 Map 是否為空(即不包含任何 key-value 對),如果為空則返回 true。
int size() 返回該 Map 裏 key-value 對的個數
Collection values() 返回該 Map 裏所有 value 組成的 Collection

Map 集合最典型的用法就是成對地添加、删除 key-value 對,接下來即可判斷該 Map 中是否包含指定 key,也可以通過 Map 提供的 keySet() 方法獲取所有 key 組成的集合,進而遍曆 Map 中所有的 key-value 對。下面程序示範了 Map 的基本功能。

例 1
每名學生都有屬於自己的唯一編號,即學號。在畢業時需要將該學生的信息從系統中移除。

下面編寫 Java 程序,使用 HashMap 來存儲學生信息,其鍵為學生學號,值為姓名。畢業時,需要用戶輸入學生的學號,並根據學號進行删除操作。具體的實現代碼如下:

public class Test09 {

public static void main(String[] args) {

HashMap users = new HashMap();
users.put("11", "張浩太"); // 將學生信息鍵值對存儲到Map中
users.put("22", "劉思誠");
users.put("33", "王强文");
users.put("44", "李國量");
users.put("55", "王路路");
System.out.println("******** 學生列錶 ********");
Iterator it = users.keySet().iterator();
while (it.hasNext()) {

// 遍曆 Map
Object key = it.next();
Object val = users.get(key);
System.out.println("學號:" + key + ",姓名:" + val);
}
Scanner input = new Scanner(System.in);
System.out.println("請輸入要删除的學號:");
int num = input.nextInt();
if (users.containsKey(String.valueOf(num))) {
 // 判斷是否包含指定鍵
users.remove(String.valueOf(num)); // 如果包含就删除
} else {

System.out.println("該學生不存在!");
}
System.out.println("******** 學生列錶 ********");
it = users.keySet().iterator();
while (it.hasNext()) {

Object key = it.next();
Object val = users.get(key);
System.out.println("學號:" + key + ",姓名:" + val);
}
}
}

在該程序中,兩次使用 while 循環遍曆 HashMap 集合。當有學生畢業時,用戶需要輸入該學生的學號,根據學號使用 HashMap 類的 remove() 方法將對應的元素删除。程序運行結果如下所示。

注意:TreeMap 類的使用方法與 HashMap 類相同,唯一不同的是 TreeMap 類可以對鍵對象進行排序,這裏不再贅述。

Java遍曆Map集合的四種方式
Map 集合的遍曆與 List 和 Set 集合不同。Map 有兩組值,因此遍曆時可以只遍曆值的集合,也可以只遍曆鍵的集合,也可以同時遍曆。Map 以及實現 Map 的接口類(如 HashMap、TreeMap、LinkedHashMap、Hashtable 等)都可以用以下幾種方式遍曆。

1)在 for 循環中使用 entries 實現 Map 的遍曆(最常見和最常用的)。

public static void main(String[] args) {

Map<String, String> map = new HashMap<String, String>();
map.put("Java入門教程", "http://c.biancheng.net/java/");
map.put("C語言入門教程", "http://c.biancheng.net/c/");
for (Map.Entry<String, String> entry : map.entrySet()) {

String mapKey = entry.getKey();
String mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
}
}

2)使用 for-each 循環遍曆 key 或者 values,一般適用於只需要 Map 中的 key 或者 value 時使用。性能上比 entrySet 較好。

Map<String, String> map = new HashMap<String, String>();
map.put("Java入門教程", "http://c.biancheng.net/java/");
map.put("C語言入門教程", "http://c.biancheng.net/c/");
// 打印鍵集合
for (String key : map.keySet()) {

System.out.println(key);
}
// 打印值集合
for (String value : map.values()) {

System.out.println(value);
}

3)使用迭代器(Iterator)遍曆

Map<String, String> map = new HashMap<String, String>();
map.put("Java入門教程", "http://c.biancheng.net/java/");
map.put("C語言入門教程", "http://c.biancheng.net/c/");
Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {

Entry<String, String> entry = entries.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ":" + value);
}

4)通過鍵找值遍曆,這種方式的效率比較低,因為本身從鍵取值是耗時的操作。

for(String key : map.keySet()){

String value = map.get(key);
System.out.println(key+":"+value);
}

Java Collections 類操作集合詳解

Collections 類是 Java 提供的一個操作 Set、List 和 Map 等集合的工具類。Collections 類提供了許多操作集合的靜態方法,借助這些靜態方法可以實現集合元素的排序、查找替換和複制等操作。下面介紹 Collections 類中操作集合的常用方法。

排序(正向和逆向)
Collections 提供了如下方法用於對 List 集合元素進行排序。

void reverse(List list):對指定 List 集合元素進行逆向排序。
void shuffle(List list):對 List 集合元素進行隨機排序(shuffle 方法模擬了“洗牌”動作)。
void sort(List list):根據元素的自然順序對指定 List 集合的元素按昇序進行排序。
void sort(List list, Comparator c):根據指定 Comparator 產生的順序對 List 集合元素進行排序。
void swap(List list, int i, int j):將指定 List 集合中的 i 處元素和 j 處元素進行交換。
void rotate(List list, int distance):當 distance 為正數時,將 list 集合的後 distance 個元素“整體”移到前面;當 distance 為負數時,將 list 集合的前 distance 個元素“整體”移到後面。該方法不會改變集合的長度。

下面程序簡單示範了利用 Collections 工具類來操作 List 集合。

例 1
編寫一個程序,對用戶輸入的 5 個商品價格進行排序後輸出。這裏要求使用 Collections 類中 sort() 方法按從低到高的順序對其進行排序,最後將排序後的成績輸出。

具體實現代碼如下:

public class Test1 {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);
List prices = new ArrayList();
for (int i = 0; i < 5; i++) {

System.out.println("請輸入第 " + (i + 1) + " 個商品的價格:");
int p = input.nextInt();
prices.add(Integer.valueOf(p)); // 將錄入的價格保存到List集合中
}
Collections.sort(prices); // 調用sort()方法對集合進行排序
System.out.println("價格從低到高的排列為:");
for (int i = 0; i < prices.size(); i++) {

System.out.print(prices.get(i) + "\t");
}
}
}

如上述代碼,循環錄入 5 個價格,並將每個價格都存儲到已定義好的 List 集合 prices 中,然後使用 Collections 類的 sort() 方法對該集合元素進行昇序排序。最後使用 for 循環遍曆 users 集合,輸出該集合中的元素。

例 2
循環錄入 5 個商品的名稱,並按錄入時間的先後順序進行降序排序,即後錄入的先輸出。

下面編寫程序,使用 Collections 類的 reverse() 方法對保存到 List 集合中的 5 個商品名稱進行反轉排序,並輸出排序後的商品信息。具體的實現代碼如下:

public class Test2 {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);
List students = new ArrayList();
System.out.println("******** 商品信息 ********");
for (int i = 0; i < 5; i++) {

System.out.println("請輸入第 " + (i + 1) + " 個商品的名稱:");
String name = input.next();
students.add(name); // 將錄入的商品名稱存到List集合中
}
Collections.reverse(students); // 調用reverse()方法對集合元素進行反轉排序
System.out.println("按錄入時間的先後順序進行降序排列為:");
for (int i = 0; i < 5; i++) {

System.out.print(students.get(i) + "\t");
}
}
}

如上述代碼,首先循環錄入 5 個商品的名稱,並將這些名稱保存到 List 集合中,然後調用 Collections 類中的 reverse() 方法對該集合元素進行反轉排序。最後使用 for 循環將排序後的集合元素輸出。

查找、替換操作
Collections 還提供了如下常用的用於查找、替換集合元素的方法。

int binarySearch(List list, Object key):使用二分搜索法搜索指定的 List 集合,以獲得指定對象在 List 集合中的索引。如果要使該方法可以正常工作,則必須保證 List 中的元素已經處於有序狀態。
Object max(Collection coll):根據元素的自然順序,返回給定集合中的最大元素。
Object max(Collection coll, Comparator comp):根據 Comparator 指定的順序,返回給定集合中的最大元素。
Object min(Collection coll):根據元素的自然順序,返回給定集合中的最小元素。
Object min(Collection coll, Comparator comp):根據 Comparator 指定的順序,返回給定集合中的最小元素。
void fill(List list, Object obj):使用指定元素 obj 替換指定 List 集合中的所有元素。
int frequency(Collection c, Object o):返回指定集合中指定元素的出現次數。
int indexOfSubList(List source, List target):返回子 List 對象在父 List 對象中第一次出現的比特置索引;如果父 List 中沒有出現這樣的子 List,則返回 -1。
int lastIndexOfSubList(List source, List target):返回子 List 對象在父 List 對象中最後一次出現的比特置索引;如果父 List 中沒有岀現這樣的子 List,則返回 -1。
boolean replaceAll(List list, Object oldVal, Object newVal):使用一個新值 newVal 替換 List 對象的所有舊值 oldVal。

下面程序簡單示範了 Collections 工具類的用法。

例 3
編寫一個程序,要求用戶輸入 3 個商品名稱,然後使用 Collections 類中的 fill() 方法對商品信息進行重置操作,即將所有名稱都更改為“未填寫”。具體的實現代碼如下:

public class Test3 {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);
List products = new ArrayList();
System.out.println("******** 商品信息 ********");
for (int i = 0; i < 3; i++) {

System.out.println("請輸入第 " + (i + 1) + " 個商品的名稱:");
String name = input.next();
products.add(name); // 將用戶錄入的商品名稱保存到List集合中
}
System.out.println("重置商品信息,將所有名稱都更改為'未填寫'");
Collections.fill(products, "未填寫");
System.out.println("重置後的商品信息為:");
for (int i = 0; i < products.size(); i++) {

System.out.print(products.get(i) + "\t");
}
}
}

如上述代碼,首先循環錄入 3 個商品名稱,並將這些商品信息存儲到 List 集合中,然後調用 Collections 類中的 fill() 方法將該集合中的所有元素值替換為“未填寫”。最後使用 for 循環將替換後的集合元素輸出。

例 4
在一個集合中保存 4 個數據,分別輸出最大最小元素和指定數據在集合中出現的次數。

public class Test4 {

public static void main(String[] args) {

ArrayList nums = new ArrayList();
nums.add(2);
nums.add(-5);
nums.add(3);
nums.add(0);
System.out.println(nums); // 輸出:[2, -5, 3, 0]
System.out.println(Collections.max(nums)); // 輸出最大元素,將輸出 3
System.out.println(Collections.min(nums)); // 輸出最小元素,將輸出-5
Collections.replaceAll(nums, 0, 1);// 將 nums中的 0 使用 1 來代替
System.out.println(nums); // 輸出:[2, -5, 3, 1]
// 判斷-5在List集合中出現的次數,返回1
System.out.println(Collections.frequency(nums, -5));
Collections.sort(nums); // 對 nums集合排序
System.out.println(nums); // 輸出:[-5, 1, 2, 3]
// 只有排序後的List集合才可用二分法查詢,輸出3
System.out.println(Collections.binarySearch(nums, 3));
}
}

如上述代碼,向 List 集合中添加 4 個數據,然後調用 Collections 類中的 max() 和 min() 方法輸出集合中的最大最小元素,replaceAll() 替換元素,frequency() 判斷指定數據在 List 集合中出現的次數,最後用 binarySearch() 進行二分法查詢。

複制
Collections 類的 copy() 靜態方法用於將指定集合中的所有元素複制到另一個集合中。執行 copy() 方法後,目標集合中每個已複制元素的索引將等同於源集合中該元素的索引。

copy() 方法的語法格式如下:

void copy(List <? super T> dest,List<? extends T> src)
1
其中,dest 錶示目標集合對象,src 錶示源集合對象。

注意:目標集合的長度至少和源集合的長度相同,如果目標集合的長度更長,則不影響目標集合中的其餘元素。如果目標集合長度不够而無法包含整個源集合元素,程序將拋出 IndexOutOfBoundsException 异常。

例 5
在一個集合中保存了 5 個商品名稱,現在要使用 Collections 類中的 copy() 方法將其中的 3 個替換掉。具體實現的代碼如下:

public class Test5 {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);
List srcList = new ArrayList();
List destList = new ArrayList();
destList.add("蘇打水");
destList.add("木糖醇");
destList.add("方便面");
destList.add("火腿腸");
destList.add("冰紅茶");
System.out.println("原有商品如下:");
for (int i = 0; i < destList.size(); i++) {

System.out.println(destList.get(i));
}
System.out.println("輸入替換的商品名稱:");
for (int i = 0; i < 3; i++) {

System.out.println("第 " + (i + 1) + " 個商品:");
String name = input.next();
srcList.add(name);
}
// 調用copy()方法將當前商品信息複制到原有商品信息集合中
Collections.copy(destList, srcList);
System.out.println("當前商品有:");
for (int i = 0; i < destList.size(); i++) {

System.out.print(destList.get(i) + "\t");
}
}
}

如上述代碼,首先創建了兩個 List 對象 srcList 和 destList,並向 destList 集合中添加了 5 個元素,向 srcList 集合中添加了 3 個元素,然後調用 Collections 類中 copy() 方法將 srcList 集合中的全部元素複制到 destList 集合中。由於 destList 集合中含有 5 個元素,故最後兩個元素不會被覆蓋。

Java Iterator(迭代器)遍曆Collection集合元素

Iterator(迭代器)是一個接口,它的作用就是遍曆容器的所有元素,也是 Java 集合框架的成員,但它與 Collection 和 Map 系列的集合不一樣,Collection 和 Map 系列集合主要用於盛裝其他對象,而 Iterator 則主要用於遍曆(即迭代訪問)Collection 集合中的元素。

Iterator 接口隱藏了各種 Collection 實現類的底層細節,向應用程序提供了遍曆 Collection 集合元素的統一編程接口。Iterator 接口裏定義了如下 4 個方法。

boolean hasNext():如果被迭代的集合元素還沒有被遍曆完,則返回 true。
Object next():返回集合裏的下一個元素。
void remove():删除集合裏上一次 next 方法返回的元素。
void forEachRemaining(Consumer action):這是 Java 8 為 Iterator 新增的默認方法,該方法可使用 Lambda 錶達式來遍曆集合元素。

下面程序示範了通過 Iterator 接口來遍曆集合元素。

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorTest {

public static void main(String[] args) {

// 創建一個集合
Collection objs = new HashSet();
objs.add("C語言中文網Java教程");
objs.add("C語言中文網C語言教程");
objs.add("C語言中文網C++教程");
// 調用forEach()方法遍曆集合
// 獲取books集合對應的迭代器
Iterator it = objs.iterator();
while (it.hasNext()) {

// it.next()方法返回的數據類型是Object類型,因此需要强制類型轉換
String obj = (String) it.next();
System.out.println(obj);
if (obj.equals("C語言中文網C語言教程")) {

// 從集合中删除上一次next()方法返回的元素
it.remove();
}
// 對book變量賦值,不會改變集合元素本身
obj = "C語言中文網Python語言教程";
}
System.out.println(objs);
}
}

從上面代碼中可以看出,Iterator 僅用於遍曆集合,如果需要創建 Iterator 對象,則必須有一個被迭代的集合。沒有集合的 Iterator 沒有存在的價值。

注意:Iterator 必須依附於 Collection 對象,若有一個 Iterator 對象,則必然有一個與之關聯的 Collection 對象。Iterator 提供了兩個方法來迭代訪問 Collection 集合裏的元素,並可通過 remove() 方法來删除集合中上一次 next() 方法返回的集合元素。

上面程序中第 24 行代碼對迭代變量 obj 進行賦值,但當再次輸岀 objs 集合時,會看到集合裏的元素沒有任何改變。所以當使用 Iterator 對集合元素進行迭代時,Iterator 並不是把集合元素本身傳給了迭代變量,而是把集合元素的值傳給了迭代變量,所以修改迭代變量的值對集合元素本身沒有任何影響。

當使用 Iterator 迭代訪問 Collection 集合元素時,Collection 集合裏的元素不能被改變,只有通過 Iterator 的 remove() 方法删除上一次 next() 方法返回的集合元素才可以,否則將會引發“java.util.ConcurrentModificationException”异常。下面程序示範了這一點。

public class IteratorErrorTest {

public static void main(String[] args) {

// 創建一個集合
Collection objs = new HashSet();
objs.add("C語言中文網Java教程");
objs.add("C語言中文網C語言教程");
objs.add("C語言中文網C++教程");
// 獲取books集合對應的迭代器
Iterator it = objs.iterator();
while (it.hasNext()) {

String obj = (String) it.next();
System.out.println(obj);
if (obj.equals("C語言中文網C++教程")) {

// 使用Iterator迭代過程中,不可修改集合元素,下面代碼引發异常
objs.remove(obj);
}
}
}
}

上面程序中第 15 行代碼比特於 Iterator 迭代塊內,也就是在 Iterator 迭代 Collection 集合過程中修改了 Collection 集合,所以程序將在運行時引發异常。

Iterator 迭代器采用的是快速失敗(fail-fast)機制,一旦在迭代過程中檢測到該集合已經被修改(通常是程序中的其他線程修改),程序立即引發 ConcurrentModificationException 异常,而不是顯示修改後的結果,這樣可以避免共享資源而引發的潜在問題。

快速失敗(fail-fast)機制,是 Java Collection 集合中的一種錯誤檢測機制。

注意:上面程序如果改為删除“C語言中文網C語言教程”字符串,則不會引發异常。這樣可能有些讀者會“心存僥幸”地想,在迭代時好像也可以删除集合元素啊。實際上這是一種危險的行為。對於 HashSet 以及後面的 ArrayList 等,迭代時删除元素都會導致异常。只有在删除集合中的某個特定元素時才不會拋出异常,這是由集合類的實現代碼决定的,程序員不應該這麼做。

Java使用foreach循環遍曆Collection集合

《Java Iterator遍曆Collection集合元素》一節中主要講解如何使用 Iterator 接口迭代訪問 Collection 集合裏的元素,除了這個方法之外,我們還可以使用 Java 5 提供的 foreach 循環迭代訪問集合元素,而且更加便捷。如下程序示範了使用 foreach 循環來迭代訪問集合元素。

public class ForeachTest {

public static void main(String[] args) {

// 創建一個集合
Collection objs = new HashSet();
objs.add("C語言中文網Java教程");
objs.add("C語言中文網C語言教程");
objs.add("C語言中文網C++教程");
for (Object obj : objs) {

// 此處的obj變量也不是集合元素本身
String obj1 = (String) obj;
System.out.println(obj1);
if (obj1.equals("C語言中文網Java教程")) {

// 下面代碼會引發 ConcurrentModificationException 异常
objs.remove(obj);
}
}
System.out.println(objs);
}
}

上面代碼使用 foreach 循環來迭代訪問 Collection 集合裏的元素更加簡潔,這正是 JDK 1.5 的 foreach 循環帶來的優勢。與使用 Iterator 接口迭代訪問集合元素類似的是,foreach 循環中的迭代變量也不是集合元素本身,系統只是依次把集合元素的值賦給迭代變量,因此在 foreach 循環中修改迭代變量的值也沒有任何實際意義。

同樣,當使用 foreach 循環迭代訪問集合元素時,該集合也不能被改變,否則將引發 ConcurrentModificationException 异常。所以上面程序中第 14 行代碼處將引發該异常。

Java 泛型簡明教程

前面我們提到 Java 集合有個缺點,就是把一個對象“丟進”集合裏之後,集合就會“忘記”這個對象的數據類型,當再次取出該對象時,該對象的編譯類型就變成了 Object 類型(其運行時類型沒變)。

Java 集合之所以被設計成這樣,是因為集合的設計者不知道我們會用集合來保存什麼類型的對象,所以他們把集合設計成能保存任何類型的對象,只要求具有很好的通用性,但這樣做帶來如下兩個問題:

集合對元素類型沒有任何限制,這樣可能引發一些問題。例如,想創建一個只能保存 Dog 對象的集合,但程序也可以輕易地將 Cat 對象“丟”進去,所以可能引發异常。
由於把對象“丟進”集合時,集合丟失了對象的狀態信息,集合只知道它盛裝的是 Object,因此取出集合元素後通常還需要進行强制類型轉換。這種强制類型轉換既增加了編程的複雜度,也可能引發 ClassCastException 异常。
所以為了解决上述問題,從 Java 1.5 開始提供了泛型。泛型可以在編譯的時候檢查類型安全,並且所有的强制轉換都是自動和隱式的,提高了代碼的重用率。本節將詳細介紹 Java 中泛型的使用。

泛型集合
泛型本質上是提供類型的“類型參數”,也就是參數化類型。我們可以為類、接口或方法指定一個類型參數,通過這個參數限制操作的數據類型,從而保證類型轉換的絕對安全。

例 1
下面將結合泛型與集合編寫一個案例實現圖書信息輸出。

1)首先需要創建一個錶示圖書的實體類 Book,其中包括的圖書信息有圖書編號、圖書名稱和價格。Book 類的具體代碼如下:

public class Book {

private int Id; // 圖書編號
private String Name; // 圖書名稱
private int Price; // 圖書價格
public Book(int id, String name, int price) {
 // 構造方法
this.Id = id;
this.Name = name;
this.Price = price;
}
public String toString() {
 // 重寫 toString()方法
return this.Id + ", " + this.Name + "," + this.Price;
}
}

2)使用 Book 作為類型創建 Map 和 List 兩個泛型集合,然後向集合中添加圖書元素,最後輸出集合中的內容。具體代碼如下:

public class Test14 {

public static void main(String[] args) {

// 創建3個Book對象
Book book1 = new Book(1, "唐詩三百首", 8);
Book book2 = new Book(2, "小星星", 12);
Book book3 = new Book(3, "成語大全", 22);
Map<Integer, Book> books = new HashMap<Integer, Book>(); // 定義泛型 Map 集合
books.put(1001, book1); // 將第一個 Book 對象存儲到 Map 中
books.put(1002, book2); // 將第二個 Book 對象存儲到 Map 中
books.put(1003, book3); // 將第三個 Book 對象存儲到 Map 中
System.out.println("泛型Map存儲的圖書信息如下:");
for (Integer id : books.keySet()) {

// 遍曆鍵
System.out.print(id + "——");
System.out.println(books.get(id)); // 不需要類型轉換
}
List<Book> bookList = new ArrayList<Book>(); // 定義泛型的 List 集合
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
System.out.println("泛型List存儲的圖書信息如下:");
for (int i = 0; i < bookList.size(); i++) {

System.out.println(bookList.get(i)); // 這裏不需要類型轉換
}
}
}

在該示例中,第 7 行代碼創建了一個鍵類型為 Integer、值類型為 Book 的泛型集合,即指明了該 Map 集合中存放的鍵必須是 Integer 類型、值必須為 Book 類型,否則編譯出錯。在獲取 Map 集合中的元素時,不需要將books.get(id);獲取的值强制轉換為 Book 類型,程序會隱式轉換。在創建 List 集合時,同樣使用了泛型,因此在獲取集合中的元素時也不需要將bookList.get(i)代碼强制轉換為 Book 類型,程序會隱式轉換。

泛型類

除了可以定義泛型集合之外,還可以直接限定泛型類的類型參數。語法格式如下:

public class class_name<data_type1,data_type2,…>{}

其中,class_name 錶示類的名稱,data_ type1 等錶示類型參數。Java 泛型支持聲明一個以上的類型參數,只需要將類型用逗號隔開即可。

泛型類一般用於類中的屬性類型不確定的情况下。在聲明屬性時,使用下面的語句:

private data_type1 property_name1;
private data_type2 property_name2;

該語句中的 data_type1 與類聲明中的 data_type1 錶示的是同一種數據類型。

例 2
在實例化泛型類時,需要指明泛型類中的類型參數,並賦予泛型類屬性相應類型的值。例如,下面的示例代碼創建了一個錶示學生的泛型類,該類中包括 3 個屬性,分別是姓名、年齡和性別。

public class Stu<N, A, S> {

private N name; // 姓名
private A age; // 年齡
private S sex; // 性別
// 創建類的構造函數
public Stu(N name, A age, S sex) {

this.name = name;
this.age = age;
this.sex = sex;
}
// 下面是上面3個屬性的setter/getter方法
public N getName() {

return name;
}
public void setName(N name) {

this.name = name;
}
public A getAge() {

return age;
}
public void setAge(A age) {

this.age = age;
}
public S getSex() {

return sex;
}
public void setSex(S sex) {

this.sex = sex;
}
}

接著創建測試類。在測試類中調用 Stu 類的構造方法實例化 Stu 對象,並給該類中的 3 個屬性賦予初始值,最終需要輸出學生信息。測試類的代碼實現如下:

public class Test14 {

public static void main(String[] args) {

Stu<String, Integer, Character> stu = new Stu<String, Integer, Character>("張曉玲", 28, '女');
String name = stu.getName();
Integer age = stu.getAge();
Character sex = stu.getSex();
System.out.println("學生信息如下:");
System.out.println("學生姓名:" + name + ",年齡:" + age + ",性別:" + sex);
}
}

在該程序的 Stu 類中,定義了 3 個類型參數,分別使用 N、A 和 S 來代替,同時實現了這 3 個屬性的 setter/getter 方法。在主類中,調用 Stu 類的構造函數創建了 Stu 類的對象,同時指定 3 個類型參數,分別為 String、Integer 和 Character。在獲取學生姓名、年齡和性別時,不需要類型轉換,程序隱式地將 Object 類型的數據轉換為相應的數據類型。

泛型方法

到目前為止,我們所使用的泛型都是應用於整個類上。泛型同樣可以在類中包含參數化的方法,而方法所在的類可以是泛型類,也可以不是泛型類。也就是說,是否擁有泛型方法,與其所在的類是不是泛型沒有關系。

泛型方法使得該方法能够獨立於類而產生變化。如果使用泛型方法可以取代類泛型化,那麼就應該只使用泛型方法。另外,對一個 static 的方法而言,無法訪問泛型類的類型參數。因此,如果 static 方法需要使用泛型能力,就必須使其成為泛型方法。

定義泛型方法的語法格式如下:

[訪問權限修飾符] [static] [final] <類型參數列錶> 返回值類型 方法名([形式參數列錶])

例如:

public static List find(Class cs,int userId){}

一般來說編寫 Java 泛型方法,其返回值類型至少有一個參數類型應該是泛型,而且類型應該是一致的,如果只有返回值類型或參數類型之一使用了泛型,那麼這個泛型方法的使用就被限制了。下面就來定義一個泛型方法,具體介紹泛型方法的創建和使用。

例 3
使用泛型方法打印圖書信息。定義泛型方法,參數類型使用“T”來代替。在方法的主體中打印出圖書信息。代碼的實現如下:

public class Test16 {

public static <T> void List(T book) {
 // 定義泛型方法
if (book != null) {

System.out.println(book);
}
}
public static void main(String[] args) {

Book stu = new Book(1, "細學 Java 編程", 28);
List(stu); // 調用泛型方法
}
}

該程序中的 Book 類為前面示例中使用到的 Book 類。在該程序中定義了一個名稱為 List 的方法,該方法的返回值類型為 void,類型參數使用“T”來代替。在調用該泛型方法時,將一個 Book 對象作為參數傳遞到該方法中,相當於指明了該泛型方法的參數類型為 Book。

泛型的高級用法

泛型的用法非常靈活,除在集合、類和方法中使用外,本節將從三個方面介紹泛型的高級用法,包括限制泛型可用類型、使用類型通配符、繼承泛型類和實現泛型接口。

  1. 限制泛型可用類型
    在 Java 中默認可以使用任何類型來實例化一個泛型類對象。當然也可以對泛型類實例的類型進行限制,語法格式如下:

class 類名稱

其中,anyClass 指某個接口或類。使用泛型限制後,泛型類的類型必須實現或繼承 anyClass 這個接口或類。無論 anyClass 是接口還是類,在進行泛型限制時都必須使用 extends 關鍵字。

例如,在下面的示例代碼中創建了一個 ListClass 類,並對該類的類型限制為只能是實現 List 接口的類。

// 限制ListClass的泛型類型必須實現List接口
public class ListClass<T extends List> {

public static void main(String[] args) {

// 實例化使用ArrayList的泛型類ListClass,正確
ListClass<ArrayList> lc1 = new ListClass<ArrayList>();
// 實例化使用LinkedList的泛型類LlstClass,正確
ListClass<LinkedList> lc2 = new ListClass<LinkedList>();
// 實例化使用HashMap的泛型類ListClass,錯誤,因為HasMap沒有實現List接口
// ListClass<HashMap> lc3=new ListClass<HashMap>();
}
}

在上述代碼中,定義 ListClass 類時設置泛型類型必須實現 List 接口。例如,ArrayList 和 LinkedList 都實現了 List 接口,所以可以實例化 ListClass 類。而 HashMap 沒有實現 List 接口,所以在實例化 ListClass 類時會報錯。

當沒有使用 extends 關鍵字限制泛型類型時,其實是默認使用 Object 類作為泛型類型。因此,Object 類下的所有子類都可以實例化泛型類對象,如圖 1 所示的這兩種情况。

  1. 使用類型通配符
    Java 中的泛型還支持使用類型通配符,它的作用是在創建一個泛型類對象時限制這個泛型類的類型必須實現或繼承某個接口或類。

使用泛型類型通配符的語法格式如下:

泛型類名稱<? extends List>a = null;

其中,“<? extends List>”作為一個整體錶示類型未知,當需要使用泛型對象時,可以單獨實例化。

例如,下面的示例代碼演示了類型通配符的使用。

A<? extends List>a = null;
a = new A<ArrayList> (); // 正確
b = new A<LinkedList> (); // 正確
c = new A<HashMap> (); // 錯誤

在上述代碼中,同樣由於 HashMap 類沒有實現 List 接口,所以在編譯時會報錯。

  1. 繼承泛型類和實現泛型接口
    定義為泛型的類和接口也可以被繼承和實現。例如下面的示例代碼演示了如何繼承泛型類。

public class FatherClass{}
public class SonClass<T1,T2,T3> extents FatherClass{}

如果要在 SonClass 類繼承 FatherClass 類時保留父類的泛型類型,需要在繼承時指定,否則直接使用 extends FatherClass 語句進行繼承操作,此時 T1、T2 和 T3 都會自動變為 Object,所以一般情况下都將父類的泛型類型保留。

下面的示例代碼演示了如何在泛型中實現接口。

interface interface1{}
interface SubClass<T1,T2,T3> implements
Interface1{}

Java圖書信息查詢
前面詳細介紹了 Java 中各集合的使用,像 Set 集合和 List 集合等,另外,還結合泛型講解了一些高級應用。在實際開發中,泛型集合是較常用的,一般定義集合都會使用泛型的形式來定義。本節將使用泛型集合來模擬實現某圖書管理系統的查詢功能。

在圖書管理系統中為了方便管理圖書,將圖書劃分為幾個類別。每個類別下有很多圖書,每本圖書都有相對應的類別,這就具備了一對多的關系映射,即一個類別對應多本圖書。

在這種情况下就可以使用 Map 映射來存儲類別和圖書信息,其鍵為 Category(類別)類型,值為 List 類型(Book 類為圖書類),然後使用嵌套循環遍曆輸出每個類別所對應的多個圖書信息。具體的實現步驟如下。

1)創建錶示圖書類別的 Category 類,在該類中有兩個屬性:id 和 name,分別錶示編號和類別名稱,並實現了它們的 setXxx() 和 getXxx() 方法,具體內容如下:

public class Category {

private int id; // 類別編號
private String name; // 類別名稱
public Category(int id, String name) {

this.id = id;
this.name = name;
}
public String toString() {

return "所屬分類:" + this.name;
}
// 上面兩個屬性的setXxx()和getXxx()方法
public int getId() {

return id;
}
public void setId(int id) {

this.id = id;
}
public String getName() {

return name;
}
public void setName(String name) {

this.name = name;
}
}

2)創建錶示圖書明細信息的 BookInfo 類,在該類中包含 5 個屬性:id、name、price、author 和 startTime,分別錶示圖書編號、名稱、價格、作者和出版時間,同樣實現了它們的 setXxx() 和 getXxx() 方法,具體內容如下:

public class BookInfo {

private int id; // 編號
private String name; // 名稱
private int price; // 價格
private String author; // 作者
private String startTime; // 出版時間
public BookInfo(int id, String name, int price, String author, String startTime) {

this.id = id;
this.name = name;
this.price = price;
this.author = author;
this.startTime = startTime;
}
public String toString() {

return this.id + "\t\t" + this.name + "\t\t" + this.price + "\t\t" + this.author + "\t\t" + this.startTime;
}
// 上面5個屬性的 setXxx() 和 getXxx() 方法
public int getId() {
return id; }
public void setId(int id) {
this.id = id;}
public String getName() {
return name;}
public void setName(String name) {
this.name = name;}
public int getPrice() {
return price;}
public void setPrice(int price) {
this.id = price;}
public String getAuthor() {
return author;}
public void setAuthor(String author) {
this.author = author;}
public String getStartTime() {
return startTime;}
public void setStartTime(String startTime) {
this.startTime = startTime;}
}

3)創建 CategoryDao 類,在該類中定義一個泛型的 Map 映射,其鍵為 Category 類型的對象,值為 List 類型的對象,並定義 printCategoryInfo() 方法,用於打印類別和圖書明細信息。具體代碼如下:

public class CategoryDao {

// 定義泛型Map,存儲圖書信息
public static Map<Category, List<BookInfo>> categoryMap = new HashMap<Category, List<BookInfo>>();
public static void printDeptmentInfo() {

for (Category cate : categoryMap.keySet()) {

System.out.println("所屬類別:" + cate.getName());
List<BookInfo> books = categoryMap.get(cate);
System.out.println("圖書編號\t\t圖書名稱\t\t圖書價格\t\t圖書作者\t\t出版時間");
for (int i = 0; i < books.size(); i++) {

BookInfo b = books.get(i); // 獲取圖書
System.out.println(b.getId() + "\t\t" + b.getName() + "\t\t" + b.getPrice() + "\t\t" + b.getAuthor()
+ "\t\t" + b.getStartTime());
}
System.out.println();
}
}
}

4)創建測試類 Test17,在該類中定義 4 個 Deptment 對象和 8 個 People 對象,並將 8 個 People 對象分成 4 組,存儲到 4 個 List 集合中,然後將 4 個 Deptment 對象和 4 個 List 集合按照——對應的關系存儲到 DeptmentDao 類中的 peoplesMap 映射中。最後調用 DeptmentDao 類中的 printDeptmentInfo() 方法打印類別及對應的圖書信息。具體的代碼如下:

public class Test17 {

public static void main(String[] args) {

Category category1 = new Category(1, "數據庫"); // 創建類別信息
Category category2 = new Category(2, "程序設計"); // 創建類別信息
Category category3 = new Category(3, "平面設計"); // 創建類別信息
BookInfo book1 = new BookInfo(1, "細說 Java 編程", 25, "張曉玲", "2012-01-01"); // 創建圖書信息
BookInfo book2 = new BookInfo(2, "影視後期處理寶典", 78, "劉水波", "2012-10-05"); // 創建圖書信息
BookInfo book3 = new BookInfo(3, "MySQL 從入門到精通", 41, "王志亮", "2012-3-2"); // 創建圖書信息
BookInfo book4 = new BookInfo(4, "Java 從入門到精通", 27, "陳奚靜", "2012-11-01"); // 創建圖書信息
BookInfo book5 = new BookInfo(5, "SQL Server 一百例", 68, "張曉玲", "2012-01-01"); // 創建圖書信息
List<BookInfo> pList1 = new ArrayList<BookInfo>(); // 向類別 1 添加圖書
pList1.add(book1);
pList1.add(book4);
List<BookInfo> pList2 = new ArrayList<BookInfo>(); // 向類別 2 添加圖書
pList2.add(book3);
pList2.add(book5);
List<BookInfo> pList3 = new ArrayList<BookInfo>(); // 向類別 3 添加圖書
pList3.add(book2);
CategoryDao.categoryMap.put(category1, pList1);
CategoryDao.categoryMap.put(category2, pList2);
CategoryDao.categoryMap.put(category3, pList3);
CategoryDao.printDeptmentInfo();
}
}

在該程序中,使用了泛型 List 和泛型 Map 分別存儲圖書類別和特定類別下的圖書明細信息。從中可以看出使用泛型不僅减少了代碼的編寫量,也提高了類型的安全性。

Java枚舉(enum)詳解:Java聲明枚舉類型、枚舉(enum)類、EnumMap 與 EnumSet
枚舉是一個被命名的整型常數的集合,用於聲明一組帶標識符的常數。枚舉在曰常生活中很常見,例如一個人的性別只能是“男”或者“女”,一周的星期只能是 7 天中的一個等。類似這種當一個變量有幾種固定可能的取值時,就可以將它定義為枚舉類型。

在 JDK 1.5 之前沒有枚舉類型,那時候一般用接口常量來替代。而使用 Java 枚舉類型 enum 可以更貼近地錶示這種常量。

聲明枚舉
聲明枚舉時必須使用 enum 關鍵字,然後定義枚舉的名稱、可訪問性、基礎類型和成員等。枚舉聲明的語法如下:

enum-modifiers enum enumname:enum-base {

enum-body,
}

其中,enum-modifiers 錶示枚舉的修飾符主要包括 public、private 和 internal;enumname 錶示聲明的枚舉名稱;enum-base 錶示基礎類型;enum-body 錶示枚舉的成員,它是枚舉類型的命名常數。

任意兩個枚舉成員不能具有相同的名稱,且它的常數值必須在該枚舉的基礎類型的範圍之內,多個枚舉成員之間使用逗號分隔。

提示:如果沒有顯式地聲明基礎類型的枚舉,那麼意味著它所對應的基礎類型是 int。

例 1
下面代碼定義了一個錶示性別的枚舉類型 SexEnum 和一個錶示顏色的枚舉類型 Color。

public enum SexEnum {

male,female;
}
public enum Color {

RED,BLUE,GREEN,BLACK;
}

之後便可以通過枚舉類型名直接引用常量,如 SexEnum.male、Color.RED。

使用枚舉還可以使 switch 語句的可讀性更强,例如以下示例代碼:

enum Signal {

// 定義一個枚舉類型
GREEN,YELLOW,RED
}
public class TrafficLight {

Signal color = Signal.RED;
public void change() {

switch(color) {

case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}

枚舉類
Java 中的每一個枚舉都繼承自 java.lang.Enum 類。當定義一個枚舉類型時,每一個枚舉類型成員都可以看作是 Enum 類的實例,這些枚舉成員默認都被 final、public, static 修飾,當使用枚舉類型成員時,直接使用枚舉名稱調用成員即可。

所有枚舉實例都可以調用 Enum 類的方法,常用方法如錶 1 所示。

方法名稱 描述
values() 以數組形式返回枚舉類型的所有成員
valueOf() 將普通字符串轉換為枚舉實例
compareTo() 比較兩個枚舉成員在定義時的順序
ordinal() 獲取枚舉成員的索引比特置
例 2
通過調用枚舉類型實例的 values( ) 方法可以將枚舉的所有成員以數組形式返回,也可以通過該方法獲取枚舉類型的成員。

下面的示例創建一個包含 3 個成員的枚舉類型 Signal,然後調用 values() 方法輸出這些成員。

enum Signal {

// 定義一個枚舉類型
GREEN,YELLOW,RED;
}
public static void main(String[] args) {

for(int i = 0;i < Signal.values().length;i++) {

System.out.println("枚舉成員:"+Signal.values()[i]);
}
}

例 3
創建一個示例,調用valueOf() 方法獲取枚舉的一個成員,再調用 compareTo() 方法進行比較,並輸出結果。具體實現代碼如下:

public class TestEnum {

public enum Sex {

// 定義一個枚舉
male,female;
}
public static void main(String[] args) {

compare(Sex.valueOf("male")); // 比較
}
public static void compare(Sex s) {

for(int i = 0;i < Sex.values().length;i++) {

System.out.println(s + "與" + Sex.values()[i] + "的比較結果是:" + s.compareTo(Sex.values()[i]));
}
}
}

上述代碼中使用 Sex.valueOf(“male”) 取出枚舉成員 male 對應的值,再將該值與其他枚舉成員進行比較。最終輸出結果如下:

male與male的比較結果是:0
male與female的比較結果是:-1

例 4
通過調用枚舉類型實例的ordinal() 方法可以獲取一個成員在枚舉中的索引比特置。下面的示例創建一個包含 3 個成員的枚舉類型 Signal,然後調用 ordinal() 方法輸出成員及對應索引比特置。

具體實現代碼如下:

public class TestEnum1 {

enum Signal {

// 定義一個枚舉類型
GREEN,YELLOW,RED;
}
public static void main(String[] args) {

for(int i = 0;i < Signal.values().length;i++) {

System.out.println("索引" + Signal.values()[i].ordinal()+",值:" + Signal.values()[i]);
}
}
}

為枚舉添加方法
Java 為枚舉類型提供了一些內置的方法,同時枚舉常量也可以有自己的方法。此時要注意必須在枚舉實例的最後一個成員後添加分號,而且必須先定義枚舉實例。

例 5
下面的代碼創建了一個枚舉類型 WeekDay,而且在該類型中添加了自定義的方法。

enum WeekDay {

Mon("Monday"),Tue("Tuesday"),Wed("Wednesday"),Thu("Thursday"),Fri("Friday"),Sat("Saturday"),Sun("Sunday");
// 以上是枚舉的成員,必須先定義,而且使用分號結束
private final String day;
private WeekDay(String day) {

this.day = day;
}
public static void printDay(int i) {

switch(i) {

case 1:
System.out.println(WeekDay.Mon);
break;
case 2:
System.out.println(WeekDay.Tue);
break;
case 3:
System.out.println(WeekDay.Wed);
break;
case 4:
System.out.println(WeekDay.Thu);
break;
case 5:
System.out.println(WeekDay.Fri);
break;
case 6:
System.out.println(WeekDay.Sat);
break;
case 7:
System.out.println(WeekDay.Sun);
break;
default:
System.out.println("wrong number!");
}
}
public String getDay() {

return day;
}
}

上面代碼創建了 WeekDay 枚舉類型,下面遍曆該枚舉中的所有成員,並調用 printDay() 方法。示例代碼如下:

public static void main(String[] args) {

for(WeekDay day : WeekDay.values()) {

System.out.println(day+"====>" + day.getDay());
}
WeekDay.printDay(5);
}

Java 中的 enum 還可以跟 Class 類一樣覆蓋基類的方法。下面示例代碼創建的 Color 枚舉類型覆蓋了 toString() 方法。

public class Test {

public enum Color {

RED("紅色",1),GREEN("綠色",2),WHITE("白色",3),YELLOW("黃色",4);
// 成員變量
private String name;
private int index;
// 構造方法
private Color(String name,int index) {

this.name = name;
this.index = index;
}
// 覆蓋方法
@Override
public String toString() {

return this.index + "-" + this.name;
}
}
public static void main(String[] args) {

System.out.println(Color.RED.toString()); // 輸出:1-紅色
}
}

EnumMap 與 EnumSet
為了更好地支持枚舉類型,java.util 中添加了兩個新類:EnumMap 和 EnumSet。使用它們可以更高效地操作枚舉類型。

EnumMap 類
EnumMap 是專門為枚舉類型量身定做的 Map 實現。雖然使用其他的 Map(如 HashMap)實現也能完成枚舉類型實例到值的映射,但是使用 EnumMap 會更加高效。

HashMap 只能接收同一枚舉類型的實例作為鍵值,並且由於枚舉類型實例的數量相對固定並且有限,所以 EnumMap 使用數組來存放與枚舉類型對應的值,使得 EnumMap 的效率非常高。

例 6
下面是使用 EnumMap 的一個代碼示例。枚舉類型 DataBaseType 裏存放了現在支持的所有數據庫類型。針對不同的數據庫,一些數據庫相關的方法需要返回不一樣的值,例如示例中 getURL() 方法。

// 定義數據庫類型枚舉
public enum DataBaseType {

MYSQUORACLE,DB2,SQLSERVER
}
// 某類中定義的獲取數據庫URL的方法以及EnumMap的聲明
private EnumMap<DataBaseType,String>urls = new EnumMap<DataBaseType,String>(DataBaseType.class);
public DataBaseInfo() {

urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample");
urls.put(DataBaseType.MYSQL,"jdbc:mysql://localhost/mydb");
urls.put(DataBaseType.ORACLE,"jdbc:oracle:thin:@localhost:1521:sample");
urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://sql:1433;Database=mydb");
}
//根據不同的數據庫類型,返回對應的URL
// @param type DataBaseType 枚舉類新實例
// @return
public String getURL(DataBaseType type) {

return this.urls.get(type);
}

在實際使用中,EnumMap 對象 urls 往往是由外部負責整個應用初始化的代碼來填充的。這裏為了演示方便,類自己做了內容填充。

從本例中可以看出,使用 EnumMap 可以很方便地為枚舉類型在不同的環境中綁定到不同的值上。本例子中 getURL 綁定到 URL 上,在其他的代碼中可能又被綁定到數據庫驅動上去。

EnumSet 類
EnumSet 是枚舉類型的高性能 Set 實現,它要求放入它的枚舉常量必須屬於同一枚舉類型。EnumSet 提供了許多工廠方法以便於初始化,如錶 2 所示。

allOf(Class element type) 創建一個包含指定枚舉類型中所有枚舉成員的 EnumSet 對象
complementOf(EnumSet s) 創建一個與指定 EnumSet 對象 s 相同的枚舉類型 EnumSet 對象, 並包含所有 s 中未包含的枚舉成員
copyOf(EnumSet s) 創建一個與指定 EnumSet 對象 s 相同的枚舉類型 EnumSet 對象, 並與 s 包含相同的枚舉成員
noneOf(<Class elementType) 創建指定枚舉類型的空 EnumSet 對象
of(E first,e…rest) 創建包含指定枚舉成員的 EnumSet 對象
range(E from ,E to) 創建一個 EnumSet 對象,該對象包含了 from 到 to 之間的所有枚 舉成員
EnumSet 作為 Set 接口實現,它支持對包含的枚舉常量的遍曆。
for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {

doSomeThing(op);
}

Java 一對多關系示例

一個學校可以包含多個學生,一個學生屬於一個學校,那麼這就是一個典型的一對多關系,可以通過集合進行關系的錶示。下面是基於集合應用的一個示例,這個示例將作為以後 Java EE 開發的基礎。

1)定義學生類

import java.util.HashSet;
import java.util.Iterator;
public class Student {

private String name;
private int age;
private School school;
public Student(String name, int age) {

this.setName(name);
this.setAge(age);
}
public String getName() {
return name;}
public void setName(String name) {
this.name = name;}
public int getAge() {
return age;}
public void setAge(int age) {
this.age = age;}
public School getSchool() {
return school;}
public void setSchool(School school) {
this.school = school;}
// 重寫 toString() 方法
public String toString() {

return "學生姓名:" + this.name + ":年齡" + this.age;
}
}

在以上的 Student 類中包含了一個 School 屬性,錶示一個學生屬於一個學校。在程序運行時,只需要傳入 School 類的引用就可以完成這樣的關系。

2)定義學校類

import java.util.ArrayList;
import java.util.List;
public class School {

private String name;
private List<Student> allStudents; // 一個學校有多個學生
public School() {

this.allStudents = new ArrayList<Student>();// 實例化List集合
}
public School(String name) {

this();
this.setName(name);
}
public String getName() {
return name;}
public void setName(String name) {
this.name = name;}
public List<Student> getAllStudents() {
return allStudents;}
public void setAllStudents(List<Student> allStudents) {
this.allStudents = allStudents;}
// 重寫toString()方法
public String toString() {
return "學校名稱:" + this.name;}
}

在定義學校類時定義了一個 List 類型的屬性,並指定其泛型類型是 Student 類型,這樣一來就錶示在一個 School 對象中會包含多個 Student 類型的引用。

3)測試代碼,設置關系

import java.util.Iterator;
public class Test {

public static void main(String[] args) {

// 實例化學校對象
School sch = new School("清華大學");
// 實例化學生對象
Student s1 = new Student("張三", 21);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 23);
// 在學校中加入學生
sch.getAllStudents().add(s1);
sch.getAllStudents().add(s2);
sch.getAllStudents().add(s3);
// 一個學生屬於一個學校
s1.setSchool(sch);
s2.setSchool(sch);
s3.setSchool(sch);
// 輸出學校信息
System.out.println(sch);
// 實例化Iterator對象,用於輸出全部的學生信息
Iterator<Student> ite = sch.getAllStudents().iterator();
while (ite.hasNext()) {

System.out.println("\t" + ite.next());
}
}
}

以上代碼先分別實例化了 School 及 Student 類的對象,之後通過兩個類中的屬性保存彼此的引用關系,從而形成了一個學校有多個學生,一個學生屬於一個學校的一對多關系。

Java 多對多關系示例

使用集合不僅可以錶示一對一的關系,也可以錶示多對多的關系。例如,一個學生可以選多門課程,一門課程可以有多個學生參加,那麼這就是一個典型的多對多關系。

要完成上面要求,首先應該定義兩個類,分別是學生信息(Student)類、課程信息(Course)類。在學生類中存在一個集合,保存全部的課程。同樣,在課程類中也要存在一個集合,保存全部的學生。

1)定義學生類

public class Student {

private String name;
private int age;
private List<Course> allCourses; // 定義集合保存全部課程
private Student() {

this.allCourses = new ArrayList<Course>();// 實例化List集合
}
// 通過構造方法設置內容
public Student(String name, int age) {

// 調用無參構造
this();
this.setName(name);
this.setAge(age);
}
public String getName() {

return name;
}
public void setName(String name) {

this.name = name;
}
public int getAge() {

return age;
}
public void setAge(int age) {

this.age = age;
}
public List<Course> getAllCourses() {

return allCourses;
}
public void setAllCourses(List<Course> allCourses) {

this.allCourses = allCourses;
}
// 重寫toString()方法
public String toString() {

return "學生姓名:" + this.name + ":年齡" + this.age;
}
}

在學生類中存在一個 allCourses 的 List 集合,這樣在程序運行時,一個學生類中可以保存多個 Course 對象。

2)定義課程類

public class Course {

private String name;
private int credit;
// 定義集合保存多個學生
private List<Student> allStudents;
private Course() {

// 實例化List集合
this.allStudents = new ArrayList<Student>();
}
public Course(String name, int credit) {

this();
this.setName(name);
this.setCredit(credit);
}
public String getName() {

return name;
}
public void setName(String name) {

this.name = name;
}
public int getCredit() {

return credit;
}
public void setCredit(int credit) {

this.credit = credit;
}
public List<Student> getAllStudents() {

return allStudents;
}
public void setAllStudents(List<Student> allStudents) {

this.allStudents = allStudents;
}
// 重寫toString()方法
public String toString() {

return "課程名稱" + this.name + ";課程學分" + this.credit;
}
}

課程類與學生類一樣,都定義了一個 List 集合,用於保存多個學生信息。

3)測試程序

public class TestMore {

public static void main(String[] args) {

// 實例化課程對象
Course c1 = new Course("英語", 3);
Course c2 = new Course("計算機", 5);
// 實例化學生對象
Student s1 = new Student("張三", 20);
Student s2 = new Student("李四", 21);
Student s3 = new Student("王五", 22);
Student s4 = new Student("趙六", 23);
Student s5 = new Student("孫七", 24);
Student s6 = new Student("錢八", 25);
// 第一門課程有3個人參加,向課程中增加3個學生信息,同時向學生中增加課程信息
c1.getAllStudents().add(s1);
c1.getAllStudents().add(s2);
c1.getAllStudents().add(s6);
s1.getAllCourses().add(c1);
s2.getAllCourses().add(c1);
s6.getAllCourses().add(c1);
// 第二門課程有6個人參加,向課程中增加6個學生信息,同時向學生中添加課程信息
// 向課程中增加學生信息
c2.getAllStudents().add(s1);
c2.getAllStudents().add(s2);
c2.getAllStudents().add(s3);
c2.getAllStudents().add(s4);
c2.getAllStudents().add(s5);
c2.getAllStudents().add(s6);
// 像學生中增加課程信息
s1.getAllCourses().add(c2);
s2.getAllCourses().add(c2);
s3.getAllCourses().add(c2);
s4.getAllCourses().add(c2);
s5.getAllCourses().add(c2);
s6.getAllCourses().add(c2);
// 輸出一門課程的信息,觀察一門課程有多少個學生參加
System.out.println(c1); // 輸出第一門課程
Iterator<Student> iter1 = c1.getAllStudents().iterator();
// 迭代輸出
while (iter1.hasNext()) {

Student s = iter1.next();
System.out.println("\t" + s);
}
// 輸出一個學生參加的課程信息,觀察有多少門課程
System.out.println(s6);
Iterator<Course> iter2 = s6.getAllCourses().iterator();
while (iter2.hasNext()) {

// 取得所參加的課程
Course c = iter2.next();
// 輸出課程信息
System.out.println("\t" + c);
}
}
}

從程序來看,設計關系的地方較為複雜,因為現在的程序采用的是雙向的處理關系,所以學生在選擇一個課程時,除了課程中要添加學生,在學生中也要添加課程信息。在輸出課程信息時,可以通過課程對象中的集合找到參加此課程的全部學生信息,也可以通過學生找到全部參加的課程信息。

hashCode() 返回當前集合的哈希碼值

import java.util.ArrayList;
import java.util.Collection;
import java.util.Arrays;
Collection c = new ArrayList();
c.add(1234);
c.add("abc");
Object[] o = c.toArray();
System.out.println(Arrays.toString(o));
import java.util.Arrays;
import java.util.List;
// 數組中的 123 是 Integer 類型的,而不是 int 類型的。
// Object 類型中只能裝對象,不能裝基本類型,基本數據類型都會自動進行自動裝箱
Object [] objects = new Object[]{
123,"abc",false};
List<Object> objects = Arrays.asList(objects); // 將數組轉換為集合
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.ArrayList;
Collection<String> c = new ArrayList<>();
Collection<String> t = new ArrayList<>();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
c.remove("a");
c.contains("a");
c.size();
c.clear();
c.isEmpty();
for(String s : c){

System.out.print(s + " ");
}
Iterator it =c.iterator();
while(it.hasNext()){

System.out.print(it.next() + " ");
}
System.out.println();
t.addAll(c);
t.add("e");
t.add("f");
t.removeAll(c); // 差集
t.retainAll(c); // 交集
t.containsAll(c); //true
Object[] obj = c.toArray();
System.out.println(Arrays.toString(obj));
版权声明:本文为[Yake1965]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201072020427919.html