## 集合概述
在前面的章節中介紹過在程序中可以通過數組來保存多個對象,但在某些情況下無法確定到底需要保存多少個對象,此時數組將不再適用,因為數組的長度不可變。例如,要保存一個學校的學生信息,由于不停有新生來報道,同時也有學員畢業離開學校,這時學生的數目很難確定。為了保存這些數目不確定的對象,JDK 中提供了一系列特殊的類,這些類可以存儲任意類型的對象,并且長度可變,統稱為集合。這些類都位于java.util包中,在使用時一定要注意導包的問題,否則會出現異常。集合按照其存儲結構可以分為兩大類,即單列集合Collection和雙列集合Map,這兩種集合的特點具體如下。
- Collection:單列集合類的根接口,用于存儲一系列符合某種規則的元素,它有兩個重要的子接口,分別是List和Set。其中,List的特點是元素有序、元素可重復。Set的特點是元素無序并且不可重復。List接口的主要實現類有ArrayList和LinkedList,Set接口的主要實現類有HashSet和TreeSet。
- Map:雙列集合類的根接口,用于存儲具有鍵(Key)、值(Value)映射關系的元素,每個元素都包含一對鍵值,在使用Map集合時可以通過指定的Key找到對應的Value,例如根據一個學生的學號就可以找到對應的學生。Map接口的主要實現類有HashMap和TreeMap。
從上面的描述可以看出JDK中提供了豐富的集合類庫,為了便于初學者進行系統地學習,接下來通過一張圖來描述整個集合類的繼承體系。

圖中列出了程序中常用的一些集合類,其中,虛線框里填寫的都是接口類型,而實線框里填寫的都是具體的實現類。
## Collection接口
Collection是所有單列集合的父接口,因此在Collection中定義了單列集合(List和Set)通用的一些方法,這些方法可用于操作所有的單列集合。
Collection接口的方法

## List接口
#### List接口簡介
List接口繼承自Collection接口,是單列集合的一個重要分支,習慣性地會將實現了List接口的對象稱為List集合。在List集合中允許出現重復的元素,所有的元素是以一種線性方式進行存儲的,在程序中可以通過索引來訪問集合中的指定元素。另外,List集合還有一個特點就是元素有序,即元素的存入順序和取出順序一致。
List作為Collection集合的子接口,不但繼承了Collection接口中的全部方法,而且還增加了一些根據元素索引來操作集合的特有方法,如表所示。

表中列舉了List集合中的常用方法,所有的List實現類都可以通過調用這些方法來對集合元素進行操作。
#### ArrayList集合
ArrayList是List接口的一個實現類,它是程序中最常見的一種集合。在ArrayList內部封裝了一個長度可變的數組對象,當存入的元素超過數組長度時,ArrayList會在內存中分配一個更大的數組來存儲這些元素,因此可以將ArrayList集合看作一個長度可變的數組。
ArrayList集合中大部分方法都是從父類Collection和List繼承過來的,其中add()方法和get()方法用于實現元素的存取,接下來通過一個案例來學習ArrayList集合如何存取元素,如例所示。
```java
import java.util.ArrayList;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 創建ArrayList 集合
list.add("stu1"); // 向集合中添加元素
list.add("stu2");
list.add("stu3");
list.add("stu4");
System.out.println("集合的長度: " + list.size()); // 獲取集合中元素的個數
System.out.println("第2個元素是: " + list.get(1)); // 取出并打印指定位置的元素
}
}
```
運行結果:
```
集合的長度: 4
第2個元素是: stu2
```
例中,首先調用add(Object o)方法向ArrayList集合添加了4個元素,然后調用size()方法獲取集合中元素個數,最后通過調用ArrayList的get(int index)方法取出指定索引位置的元素。從運行結果可以看出,索引位置為1的元素是集合中的第二個元素,這就說明集合和數組一樣,索引的取值范圍是從0開始的,最后一個索引是size-1,在訪問元素時一定要注意索引不可超出此范圍,否則會拋出角標越界異常IndexOutOfBoundsException。
由于ArrayList集合的底層是使用一個數組來保存元素,在增加或刪除指定位置的元素時,會導致創建新的數組,效率比較低,因此不適合做大量的增刪操作。但這種數組的結構允許程序通過索引的方式來訪問元素,因此使用ArrayList集合查找元素很便捷。
#### LinkedList集合
ArrayList集合在查詢元素時速度很快,但在增刪元素時效率較低,為了克服這種局限性,可以使用List接口的另一個實現類LinkedList。該集合內部維護了一個雙向循環鏈表,鏈表中的每一個元素都使用引用的方式來記住它的前一個元素和后一個元素,從而可以將所有的元素彼此連接起來。當插入一個新元素時,只需要修改元素之間的這種引用關系即可,刪除一個節點也是如此。正因為這樣的存儲結構,所以LinkedList集合對于元素的增刪操作具有很高的效率,LinkedList集合添加元素和刪除元素的過程如圖所示。

圖中,通過兩張圖描述了LinkedList集合新增元素和刪除元素的過程。其中,左圖為新增一個元素,圖中的元素1和元素2在集合中彼此為前后關系,在它們之間新增一個元素時,只需要讓元素1記住它后面的元素是新元素,讓元素2記住它前面的元素為新元素就可以了。右圖為刪除元素,要想刪除元素1與元素2之間的元素3,只需要讓元素1與元素2變成前后關系就可以了。
LinkedList集合除了具備增刪元素效率高的特點,還專門針對元素的增刪操作定義了一些特有的方法,如表所示。

表中,列出的方法主要針對集合中的元素進行增加、刪除和獲取操作,接下來通過一個案例來學習這些方法的使用,如例所示。
```java
import java.util.LinkedList;
public class Example {
public static void main(String[] args) {
LinkedList link = new LinkedList(); // 創建LinkedList 集合
link.add("stu1");
link.add("stu2");
link.add("stu3");
link.add("stu4");
System.out.println(link.toString()); // 取出并打印該集合中的元素
link.add(3, "Student"); // 向該集合中指定位置插入元素
link.addFirst("First"); // 向該集合第一個位置插入元素
System.out.println(link);
System.out.println(link.getFirst()); // 取出該集合中第一個元素
link.remove(3); // 移除該集合中指定位置的元素
link.removeFirst(); // 移除該集合中第一個元素
System.out.println(link);
}
}
```
運行結果:
```
[stu1, stu2, stu3, stu4]
[First, stu1, stu2, stu3, Student, stu4]
First
[stu1, stu2, Student, stu4]
```
例中,首先在LinkedList集合中存入4個元素,然后通過add(int index,Object o)和addFirst(Object o)方法分別在集合的指定位置和第一個位置(索引0位置)插入元素,最后使用remove(intindex)和removeFirst()方法將指定位置和集合中的第一個元素移除,這樣就完成了元素的增刪操作。由此可見,使用LinkedList對元素進行增刪操作是非常便捷的。
#### Iterator接口
在程序開發中,經常需要遍歷集合中的所有元素。針對這種需求,JDK 專門提供了一個接口Iterator。Iterator接口也是Java集合框架中的一員,但它與Collection、Map接口有所不同,Collection接口與Map接口主要用于存儲元素,而Iterator主要用于迭代訪問(即遍歷)Collection中的元素,因此Iterator對象也被稱為迭代器。
接下來通過一個案例來學習如何使用Iterator迭代集合中的元素,如例所示。
```java
import java.util.ArrayList;
import java.util.Iterator;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 創建ArrayList 集合
list.add("data_1"); // 向該集合中添加字符串
list.add("data_2");
list.add("data_3");
list.add("data_4");
Iterator it = list.iterator(); // 獲取Iterator 對象
while (it.hasNext()) { // 判斷ArrayList 集合中是否存在下一個元素
Object obj = it.next(); // 取出ArrayList 集合中的元素
System.out.println(obj);
}
}
}
```
運行結果:
```
data_1
data_2
data_3
data_4
```
例中演示的是Iterator遍歷集合的整個過程。當遍歷元素時,首先通過調用ArrayList集合的iterator()方法獲得迭代器對象,然后使用hasNext()方法判斷集合中是否存在下一個元素,如果存在,則調用next()方法將元素取出,否則說明已到達了集合末尾,停止遍歷元素。需要注意的是,在通過next()方法獲取元素時,必須保證要獲取的元素存在,否則,會拋出NoSuchElementException異常。
Iterator迭代器對象在遍歷集合時,內部采用指針的方式來跟蹤集合中的元素,為了讓初學者能更好地理解迭代器的工作原理,接下來通過一個圖例來演示Iterator對象迭代元素的過程,如圖所示。

圖中,在調用Iterator的next()方法之前,迭代器的索引位于第一個元素之前,不指向任何元素,當第一次調用迭代器的next()方法后,迭代器的索引會向后移動一位,指向第一個元素并將該元素返回,當再次調用next()方法時,迭代器的索引會指向第二個元素并將該元素返回,依此類推,直到hasNext()方法返回false,表示到達了集合的末尾,終止對元素的遍歷。
需要特別說明的是,當通過迭代器獲取ArrayList集合中的元素時,都會將這些元素當作Object類型來看待,如果想得到特定類型的元素,則需要進行強制類型轉換。
#### JDK5.0新特性 - foreach循環
雖然Iterator可以用來遍歷集合中的元素,但寫法上比較煩瑣,為了簡化書寫,從JDK5.0開始,提供了foreach循環。foreach循環是一種更加簡潔的for循環,也稱增強for循環。foreach循環用于遍歷數組或集合中的元素,其具體語法格式如下:
```
for(容器中元素類型臨時變量: 容器變量) {
執行語句
}
```
從上面的格式可以看出,與for循環相比,foreach循環不需要獲得容器的長度,也不需要根據索引訪問容器中的元素,但它會自動遍歷容器中的每個元素。接下來通過一個案例對foreach循環進行詳細講解,如例所示。
```java
import java.util.ArrayList;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 創建ArrayList 集合
list.add("Jack"); // 向ArrayList 集合中添加字符串元素
list.add("Rose");
list.add("Tom");
for (Object obj : list) { // 使用foreach 循環遍歷ArrayList 對象
System.out.println(obj); // 取出并打印ArrayList 集合中的元素
}
}
}
```
運行結果:
```
Jack
Rose
Tom
```
通過例可以看出,foreach循環在遍歷集合時語法非常簡潔,沒有循環條件,也沒有迭代語句,所有這些工作都交給虛擬機去執行了。foreach循環的次數是由容器中元素的個數決定的,每次循環時,foreach中都通過變量將當前循環的元素記住,從而將集合中的元素分別打印出來。
#### ListIterator接口
上面講解的Iterator迭代器提供了hasNext()方法和next()方法,通過這兩個方法可以實現集合中元素的迭代,迭代的方向是從集合中的第一個元素向最后一個元素迭代,也就是所謂的正向迭代。為了使迭代方式更加多元化,JDK 中還定義了一個ListIterator迭代器,它是Iterator的子類,該類在父類的基礎上增加了一些特有的方法,如表所示。

從表可以看出ListIterator中提供了hasPrevious()方法和previous()方法,通過這兩個方法可以實現反向迭代元素,另外還提供了add()方法用于增加元素。接下來通過一個案例來學習ListIterator迭代器的使用,如例所示。
```java
import java.util.ArrayList;
import java.util.ListIterator;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("data_1");
list.add("data_2");
list.add("data_3");
System.out.println(list);
ListIterator it = list.listIterator(list.size()); // 獲得ListIterator 對象
while (it.hasPrevious()) { // 判斷該對象中是否有上一個元素
Object obj = it.previous(); // 迭代該對象的上一個元素
System.out.print(obj + " "); // 獲取并打印該對象中的元素
}
}
}
```
運行結果:
```
[data_1, data_2, data_3]
data_3 data_2 data_1
```
例中,演示的是ListIterator從后向前遍歷集合的過程。在使用listIterator(int index)方法獲得ListIterator對象時,需要傳遞一個int類型的參數指定迭代的起始位置,本例中傳入的是集合的長度,表示以集合的最后一個元素開始迭代,之后再使用hasPrevious()方法判斷是否存在上一個元素,如果存在,則通過previous()方法將元素取出;否則,則表示到達了集合的末尾,沒有要遍歷的元素。在這個過程中,如果想增加元素同樣不能調用集合對象的add()方法,此時需要使用ListIterator提供的add()方法,否則會出現并發修改異常ConcurrentModificationException。需要注意的是,ListIterator迭代器只能用于List集合。
#### Enumeration接口
前面我們講過在遍歷集合時可以使用Iterator接口,但在JDK1.2 以前還沒有Iterator接口的時候,遍歷集合需要使用Enumeration接口,它的用法和Iterator類似。由于很多程序中依然在使用Enumeration,因此了解該接口的用法是很有必要的。JDK中提供了一個Vector集合,該集合是List接口的一個實現類,用法與ArrayList完全相同,區別在于Vector集合是線程安全的,而ArrayList集合是線程不安全的。在Vector類中提供了一個elements()方法用于返回Enumeration對象,通過Enumeration對象就可以遍歷該集合中的元素。接下來通過一個案例來演示如何使用Enumeration對象遍歷Vector集合,如例所示。
```java
import java.util.Enumeration;
import java.util.Vector;
public class Example {
public static void main(String[] args) {
Vector v = new Vector(); // 創建Vector 對象
v.add("Jack"); // 向該Vector 對象中添加元素
v.add("Rose");
v.add("Tom");
Enumeration en = v.elements(); // 獲得Enumeration 對象
while (en.hasMoreElements()) { // 判斷該對象是否有更多元素
Object obj = en.nextElement(); // 取出該對象的下一個元素
System.out.println(obj);
}
}
}
```
運行結果:
```
Jack
Rose
Tom
```
例中,首先創建了一個Vector集合并通過調用add()方法向集合添加三個元素,然后調用elements()方法返回一個Enumeration對象,例程中的第9~12行代碼使用一個while循環對集合中的元素進行迭代,其過程與Iterator迭代的過程類似,通過hasMoreElements()方法循環判斷是否存在下一個元素,如果存在,則通過nextElement()方法逐一取出每個元素。
## Set接口
#### Set接口簡介
Set接口和List接口一樣,同樣繼承自Collection接口,它與Collection接口中的方法基本一致,并沒有對Collection接口進行功能上的擴充,只是比Collection接口更加嚴格了。與List接口不同的是,Set接口中元素無序,并且都會以某種規則保證存入的元素不出現重復。
Set接口主要有兩個實現類,分別是HashSet和TreeSet。其中,HashSet是根據對象的哈希值來確定元素在集合中的存儲位置,因此具有良好的存取和查找性能。TreeSet則是以二叉樹的方式來存儲元素,它可以實現對集合中的元素進行排序。接下來圍繞Set集合的這兩個實現類詳細地進行講解。
#### HashSet集合
HashSet是Set接口的一個實現類,它所存儲的元素是不可重復的,并且元素都是無序的。當向HashSet集合中添加一個對象時,首先會調用該對象的hashCode()方法來確定元素的存儲位置,然后再調用對象的equals()方法來確保該位置沒有重復元素。Set集合與List集合存取元素的方式都一樣,在此不再進行詳細的講解,接下來通過一個案例來演示HashSet集合的用法,如例所示。
```java
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
public class Example {
public static void main(String[] args) {
HashSet set = new HashSet(); // 創建HashSet 集合
set.add("Jack"); // 向該Set 集合中添加字符串
set.add("Eve");
set.add("Rose");
set.add("Rose"); // 向該Set 集合中添加重復元素
Iterator it = set.iterator(); // 獲取Iterator 對象
while (it.hasNext()) { // 通過while 循環,判斷集合中是否有元素
Object obj = it.next(); // 如果有元素,就通過迭代器的next()方法獲取元素
System.out.println(obj);
}
}
}
```
運行結果:
```
Eve
Rose
Jack
```
例中,首先通過add()方法向HashSet集合依次添加了四個字符串,然后通過Iterator迭代器遍歷所有的元素并輸出打印。從打印結果可以看出取出元素的順序與添加元素的順序并不一致,并且重復存入的字符串對象“Rose”被去除了,只添加了一次。
HashSet集合之所以能確保不出現重復的元素,是因為它在存入元素時做了很多工作。當調用HashSet集合的add()方法存入元素時,首先調用當前存入對象的hashCode()方法獲得對象的哈希值,然后根據對象的哈希值計算出一個存儲位置。如果該位置上沒有元素,則直接將元素存入,如果該位置上有元素存在,則會調用equals()方法讓當前存入的元素依次和該位置上的元素進行比較,如果返回的結果為false就將該元素存入集合,返回的結果為true則說明有重復元素,就將該元素舍棄。整個存儲的流程如圖所示。

根據前面的分析不難看出,當向集合中存入元素時,為了保證HashSet正常工作,要求在存入對象時,重寫該類中的hashCode()和equals()方法。例中將字符串存入HashSet時,String類已經重寫了hashCode()和equals()方法。但是如果將Student對象存入HashSet,結果又如何呢? 接下來通過一個案例來進行演示,如例所示。
```java
import java.util.HashSet;
class Student {
String id;
String name;
public Student(String id, String name) { // 創建構造方法
this.id = id;
this.name = name;
}
public String toString() { // 重寫toString()方法
return id + ":" + name;
}
}
public class Example {
public static void main(String[] args) {
HashSet hs = new HashSet(); // 創建HashSet 集合
Student stu1 = new Student("1", "Jack"); // 創建Student 對象
Student stu2 = new Student("2", "Rose");
Student stu3 = new Student("2", "Rose");
hs.add(stu1);
hs.add(stu2);
hs.add(stu3);
System.out.println(hs);
}
}
```
運行結果:
```
[2:Rose, 1:Jack, 2:Rose]
```
在例中,向HashSet集合存入三個Student對象,并將這三個對象迭代輸出。圖所示的運行結果中出現了兩個相同的學生信息“2:Rose”,這樣的學生信息應該被視為重復元素,不允許同時出現在HashSet集合中。之所以沒有去掉這樣的重復元素是因為在定義Student類時沒有重寫hashCode()和equals()方法。接下來針對例中的Student類進行改寫,假設id相同的學生就是同一個學生,改寫后的代碼如例所示。
```java
import java.util.HashSet;
class Student {
private String id;
private String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
// 重寫toString()方法
public String toString() {
return id + ":" + name;
}
// 重寫hashCode 方法
public int hashCode() {
return id.hashCode(); // 返回id 屬性的哈希值
}
// 重寫equals 方法
public boolean equals(Object obj) {
if (this == obj) { // 判斷是否是同一個對象
return true; // 如果是,直接返回true
}
if (!(obj instanceof Student)) { // 判斷對象是為Student 類型
return false; // 如果對象不是Student 類型,返回false
}
Student stu = (Student) obj; // 將對象強轉為Student 類型
boolean b = this.id.equals(stu.id); // 判斷id 值是否相同
return b; // 返回判斷結果
}
}
public class Example {
public static void main(String[] args) {
HashSet hs = new HashSet(); // 創建HashSet 對象
Student stu1 = new Student("1", "Jack"); // 創建Student 對象
Student stu2 = new Student("2", "Rose");
Student stu3 = new Student("2", "Rose");
hs.add(stu1); // 向集合存入對象
hs.add(stu2);
hs.add(stu3);
System.out.println(hs); // 打印集合中的元素
}
}
```
運行結果:
```
[1:Jack, 2:Rose]
```
在例7-11 中,Student類重寫了Object類的hashCode()和equals()方法。在hashCode()方法中返回id屬性的哈希值,在equals()方法中比較對象的id屬性是否相等,并返回結果。當調用HashSet集合的add()方法添加stu3對象時,發現它的哈希值與stu2對象相同,而且stu2.equals(stu3)返回true,HashSet集合認為兩個對象相同,因此重復的Student對象被成功去除了。
#### TreeSet集合
TreeSet是Set接口的另一個實現類,它內部采用自平衡的排序二叉樹來存儲元素,這樣的結構可以保證TreeSet集合中沒有重復的元素,并且可以對元素進行排序。所謂二叉樹就是說每個節點最多有兩個子節點的有序樹,每個節點及其子節點組成的樹稱為子樹,通常左側的子節點稱為“左子樹”,右側的子節點稱為“右子樹”,二叉樹中元素的存儲結構如圖所示。

在實際應用中,二叉樹分為很多種,如排序二叉樹、平衡二叉樹等。TreeSet集合內部使用的是自平衡的排序二叉樹,它的特點是存儲的元素會按照大小排序,并能去除重復元素。例如向一個二叉樹中存入8個元素,依次為13、8、17、17、1、11、15、25,如果以排序二叉樹的方式來存儲,在集合中的存儲結構會形成一個樹狀結構,如圖所示。

從圖可以看出,在向TreeSet集合依次存入元素時,首先將第1個存入的元素放在二叉樹的最頂端,之后存入的元素與第一個元素比較,如果小于第一個元素就將該元素放在左子樹上,如果大于第1個元素,就將該元素放在右子樹上,依此類推,按照左子樹元素小于右子樹元素的順序進行排序。當二叉樹中已經存入一個17的元素時,再向集合中存入一個為17的元素時,TreeSet會將把重復的元素去掉。
了解了二叉樹存放元素的原理,接下來通過一個案例來演示TreeSet對元素的排序效果,如例所示。
```java
import java.util.Iterator;
import java.util.TreeSet;
public class Example {
public static void main(String[] args) {
TreeSet ts = new TreeSet(); // 創建TreeSet 集合
ts.add("Jack"); // 向TreeSet 集合中添加元素
ts.add("Helena");
ts.add("Helena");
ts.add("Eve");
Iterator it = ts.iterator(); // 獲取Iterator 對象
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
```
運行結果:
```
Eve
Helena
Jack
```
從圖可以看出,通過迭代器Iterator迭代出的字符串元素按照字母表的順序打印了出來,這些元素之所以能夠排序是因為每次向TreeSet集合中存入一個元素時,就會將該元素與其他元素進行比較,最后將它插入到有序的對象序列中。集合中的元素在進行比較時,都會調用compareTo()方法,該方法是Comparable接口中定義的,因此要想對集合中的元素進行排序,就必須實現Comparable接口。JDK 中大部分的類都實現Comparable接口,擁有了接口中的CompareTo()方法,如Integer、Double和String等。
在TreeSet集合中存放Student類型對象時,如果Student類沒有實現Comparable接口,則Student類型的對象將不能進行比較,這時,TreeSet集合就不知道按照什么排序規則對Student對象進行排序,最終導致程序報錯。因此,為了在TreeSet集合中存放Student對象,必須使Student類實現Comparable接口,如例所示。
```java
import java.util.Iterator;
import java.util.TreeSet;
class Student implements Comparable { // 定義Student 類實現Comparable 接口
String name;
int age;
public Student(String name, int age) {// 創建構造方法
this.name = name;
this.age = age;
}
public String toString() { // 重寫Object 類的toString()方法,返回描述信息
return name + ":" + age;
}
public int compareTo(Object obj) { // 重寫Comparable 接口的compareTo 方法
Student s = (Student) obj; // 將比較對象強轉為Student 類型
if (this.age - s.age > 0) { // 定義比較方式
return 1;
}
if (this.age - s.age == 0) {
return this.name.compareTo(s.name); // 將比較結果返回
}
return -1;
}
}
public class Example {
public static void main(String[] args) {
TreeSet ts = new TreeSet(); // 創建TreeSet 集合
ts.add(new Student("Jack", 19)); // 向集合中添加元素
ts.add(new Student("Rose", 18));
ts.add(new Student("Tom", 19));
ts.add(new Student("Rose", 18));
Iterator it = ts.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
```
運行結果:
```
Rose:18
Jack:19
Tom:19
```
例中,Student 類實現了Comparable 接口中的compareTo()方法。在compareTo()方法中,首先先針對age值進行比較,根據比較結果返回-1和1,當age相同時,再對name進行比較。因此,從運行結果可以看出,學生首先按照年齡排序,年齡相同時會按照姓名排序。
有時候,定義的類沒有實現Comparable接口或者實現了Comparable接口而不想按照定義的compareTo()方法進行排序。例如,希望字符串可以按照長度來進行排序,這時,可以通過自定義比較器的方式對TreeSet集合中的元素排序,即實現Comparator接口,在創建TreeSet集合時指定比較器。接下來通過一個案例來實現TreeSet集合中字符串按照長度進行排序,如例所示。
```java
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class MyComparator implements Comparator { // 定義比較器實現Comparator 接口
public int compare(Object obj1, Object obj2) { // 實現比較方法
String s1 = (String) obj1;
String s2 = (String) obj2;
int temp = s1.length() - s2.length();
return temp;
}
}
public class Example {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyComparator()); // 創建TreeSet 對象時傳入自定義比較器
ts.add("Jack"); // 向該Set 對象中添加字符串
ts.add("Helena");
ts.add("Eve");
Iterator it = ts.iterator(); // 獲取Iterator 對象
// 通過while 循環,逐漸將集合中的元素打印出來
while (it.hasNext()) {
// 如果Iterator 有元素進行迭代,則獲取元素并進行打印
Object obj = it.next();
System.out.println(obj);
}
}
}
```
運行結果:
```
Eve
Jack
Helena
```
例中,定義了一個MyComparator類實現了Comparator接口,在compare()方法中實現元素的比較,這就相當于定義了一個比較器。在創建TreeSet集合時,將MyComparator比較器對象傳入,當向集合中添加元素時,比較器對象的compare()方法就會被自動調用,從而使存入TreeSet集合中的字符串按照長度進行排序。
## Map接口
#### Map接口簡介
在現實生活中,每個人都有唯一的身份證號,通過身份證號可以查詢到這個人的信息,這兩者是一對一的關系。在應用程序中,如果想存儲這種具有對應關系的數據,則需要使用JDK中提供的Map接口。Map接口是一種雙列集合,它的每個元素都包含一個鍵對象Key和一個值對象Value,鍵和值對象之間存在一種對應關系,稱為映射。從Map集合中訪問元素時,只要指定了Key,就能找到對應的Value。為了便于Map接口的學習,接下來首先了解一下Map接口中定義的一些通用方法。

表中,列出了一系列方法用于操作Map。其中,put(Object key,Object value)和get(Object key)方法分別用于向Map中存入元素和取出元素;containsKey(Object key)和containsValue(Object value)方法分別用于判斷Map中是否包含某個指定的鍵或值;keySet()和values()方法分別用于獲取Map中所有的鍵和值。
Map接口提供了大量的實現類,最常用的有HashMap和TreeMap,接下來針對這兩個類進行詳細地講解。
#### HashMap集合
HashMap集合是Map接口的一個實現類,它用于存儲鍵值映射關系,但必須保證不出現重復的鍵。接下來通過一個案例來學習HashMap的用法。
```java
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String[] args) {
Map map = new HashMap(); // 創建Map 對象
map.put("1", "Jack"); // 存儲鍵和值
map.put("2", "Rose");
map.put("3", "Lucy");
System.out.println("1: " + map.get("1")); // 根據鍵獲取值
System.out.println("2: " + map.get("2"));
System.out.println("3: " + map.get("3"));
}
}
```
運行結果:
```
1: Jack
2: Rose
3: Lucy
```
例中,首先通過Map的put(Object key,Object value)方法向集合中加入3個元素,然后通過Map的get(Object key)方法獲取與鍵對應的值。前面講過Map集合中的鍵具有唯一性,現在向Map集合中存儲一個相同的鍵看看會出現什么情況。現對例進行修改,在第9行代碼下面增加一行代碼,如下所示:
```java
map.put("3", "Mary");
```
運行結果:
```
1: Jack
2: Rose
3: Mary
```
從圖中可以看出,Map中仍然只有3個元素,第二次添加的值“Mary”覆蓋原來的值“Lucy”,因此證實了Map中的鍵必須是唯一的,不能重復。如果存儲了相同的鍵,后存儲的值則會覆蓋原有的值,簡而言之就是:鍵相同,值覆蓋。在程序開發中,經常需要取出Map中所有的鍵和值,那么如何遍歷Map中所有的鍵值對呢? 有兩種方式可以實現,第一種方式就是先遍歷Map集合中所有的鍵,再根據鍵獲取相應的值,接下來就通過一個案例來演示這種遍歷方式,如例所示。
```java
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Example {
public static void main(String[] args) {
Map map = new HashMap(); // 創建Map 集合
map.put("1", "Jack"); // 存儲鍵和值
map.put("2", "Rose");
map.put("3", "Lucy");
Set keySet = map.keySet(); // 獲取鍵的集合
Iterator it = keySet.iterator(); // 迭代鍵的集合
while (it.hasNext()) {
Object key = it.next();
Object value = map.get(key); // 獲取每個鍵所對應的值
System.out.println(key + ":" + value);
}
}
}
```
運行結果:
```
1:Jack
2:Rose
3:Lucy
```
例中,是第一種遍歷Map的方式。首先調用Map對象的keySet()方法,獲得存儲Map中所有鍵的Set集合,然后通過Iterator迭代Set集合的每一個元素,即每一個鍵,最后通過調用get(Stringkey)方法,根據鍵獲取對應的值。
Map集合的另外一種遍歷方式是先獲取集合中的所有的映射關系,然后從映射關系中取出鍵和值。接下來通過一個案例來演示這種遍歷方式,如例所示。
```java
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Example {
public static void main(String[] args) {
Map map = new HashMap(); // 創建Map 集合
map.put("1", "Jack"); // 存儲鍵和值
map.put("2", "Rose");
map.put("3", "Lucy");
Set entrySet = map.entrySet();
Iterator it = entrySet.iterator(); // 獲取Iterator 對象
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) (it.next()); // 獲取集合中鍵值對映射關系
Object key = entry.getKey(); // 獲取Entry 中的鍵
Object value = entry.getValue(); // 獲取Entry 中的值
System.out.println(key + ":" + value);
}
}
}
```
運行結果:
```
1:Jack
2:Rose
3:Lucy
```
例7-17中,是第二種遍歷Map的方式。首先調用Map對象的entrySet()方法獲得存儲在Map中所有映射的Set集合,這個集合中存放了Map.Entry類型的元素(Entry是Map接口內部類),每個Map.Entry對象代表Map中的一個鍵值對,然后迭代Set集合,獲得每一個映射對象,并分別調用映射對象的getKey()和getValue()方法獲取鍵和值。
在Map中,還提供了一個values()方法,通過這個方法可以直接獲取Map中存儲所有值的Collection集合,接下來通過一個案例來演示,如例所示。
```java
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Example {
public static void main(String[] args) {
Map map = new HashMap(); // 創建Map 集合
map.put("1", "Jack"); // 存儲鍵和值
map.put("2", "Rose");
map.put("3", "Lucy");
Collection values = map.values();
Iterator it = values.iterator();
while (it.hasNext()) {
Object value = it.next();
System.out.println(value);
}
}
}
```
運行結果:
```
Jack
Rose
Lucy
```
在例中,通過調用Map的values()方法獲取包含Map中所有值的Collection集合,然后迭代出集合中的每一個值。
從上面的例子可以看出,HashMap集合迭代出來元素的順序和存入的順序是不一致的。如果想讓這兩個順序一致,可以使用Java中提供的LinkedHashMap類,它是HashMap的子類,和LinkedList一樣也使用雙向鏈表來維護內部元素的關系,使Map元素迭代的順序與存入的順序一致。接下來通過一個案例來學習一下LinkedHashMap的用法,如例所示。
```java
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class Example {
public static void main(String[] args) {
Map map = new LinkedHashMap(); // 創建Map 集合
map.put("1", "Jack"); // 存儲鍵和值
map.put("2", "Rose");
map.put("3", "Lucy");
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while (it.hasNext()) {
Object key = it.next();
Object value = map.get(key); // 獲取每個鍵所對應的值
System.out.println(key + ":" + value);
}
}
}
```
運行結果:
```
1:Jack
2:Rose
3:Lucy
```
在例中,首先創建了一個LinkedHashMap集合并存入了3個元素,然后使用迭代器將元素取出。從運行結果可以看出,元素迭代出來的順序和存入的順序是一致的。
#### TreeMap集合
在JDK中,Map接口還有一個常用的實現類TreeMap。TreeMap集合是用來存儲鍵值映射關系的,其中不允許出現重復的鍵。在TreeMap中是通過二叉樹的原理來保證鍵的唯一性,這與TreeSet集合存儲的原理一樣,因此TreeMap中所有的鍵是按照某種順序排列的。接下來通過一個案例來了解TreeMap的具體用法,如例所示。
```java
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
public class Example {
public static void main(String[] args) {
TreeMap tm = new TreeMap();
tm.put("1", "Jack");
tm.put("2", "Rose");
tm.put("3", "Lucy");
Set keySet = tm.keySet(); // 獲取鍵的集合
Iterator it = keySet.iterator(); // 獲取Iterator 對象
while (it.hasNext()) { // 判斷是否存在下一個元素
Object key = it.next(); // 取出元素
Object value = tm.get(key); // 根據獲取的鍵找到對應的值
System.out.println(key + ":" + value);
}
}
}
```
運行結果:
```
1:Jack
2:Rose
3:Lucy
```
在例中,使用put()方法將3個學生的信息存入TreeMap集合,其中學號作為鍵,姓名作為值,然后對學生信息進行遍歷。從運行結果可以看出,取出的元素按照學號的自然順序進行了排序,這是因為學號是String類型,String類實現了Comparable接口,因此默認會按照自然順序進行排序。
在使用TreeMap集合時,也可以通過自定義比較器的方式對所有的鍵進行排序。接下來通過一個案例將學生對象按照學號由大到小的順序進行排序,如例所示。
```java
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
public class Example {
public static void main(String[] args) {
TreeMap tm = new TreeMap(new MyComparator()); // 傳入一個自定義比較器
tm.put("1", "Jack"); // 向集合存入學生的學號和姓名
tm.put("2", "Rose");
tm.put("3", "Lucy");
Set keySet = tm.keySet(); // 獲取鍵的集合
Iterator it = keySet.iterator(); // 獲得迭代器對象
while (it.hasNext()) {
Object key = it.next(); // 獲得一個鍵
Object value = tm.get(key); // 獲得鍵對應的值
System.out.println(key + ":" + value);
}
}
}
class MyComparator implements Comparator { // 自定義比較器
public int compare(Object obj1, Object obj2) { // 實現比較方法
String id1 = (String) obj1; // 將Object 類型的參數強轉為String 類型
String id2 = (String) obj2;
return id2.compareTo(id1); // 將比較之后的值返回
}
}
```
運行結果:
```
3:Lucy
2:Rose
1:Jack
```
例中定義了比較器MyComparator針對String類型的id進行比較,在實現compare()方法時,調用了String 對象的compareTo()方法。由于方法中返回的是“id2.compareTo(id1)”,因此最終輸出結果中的id按照與字典順序相反的順序進行了排序。
#### Properties集合
Map接口中還有一個實現類Hashtable,它和HashMap 十分相似,區別在于Hashtable是線程安全的。Hashtable存取元素時速度很慢,目前基本上被HashMap類所取代,但Hashtable類有一個子類Properties在實際應用中非常重要,Properties主要用來存儲字符串類型的鍵和值,在實際開發中,經常使用Properties集合來存取應用的配置項。假設有一個文本編輯工具,要求默認背景色是紅色,字體大小為14px,語言為中文,其配置項應該如下:
```
Backgroup-color=red
Font-size=14px
Language=chinese
```
在程序中可以使用Prorperties集合對這些配置項進行存取,接下來通過一個案例來學習,如例所示。
```java
import java.util.Enumeration;
import java.util.Properties;
public class Example {
public static void main(String[] args) {
Properties p = new Properties(); // 創建Properties 對象
p.setProperty("backgroup-color", "red");
p.setProperty("Font-size", "14px");
p.setProperty("Language", "chinese");
Enumeration names = p.propertyNames(); // 獲取Enumeration 對象所有鍵的枚舉
while (names.hasMoreElements()) { // 循環遍歷所有的鍵
String key = (String) names.nextElement();
String value = p.getProperty(key); // 獲取對應鍵的值
System.out.println(key + "=" + value);
}
}
}
```
運行結果:
```
backgroup-color=red
Language=chinese
Font-size=14px
```
例的Properties類中,針對字符串的存取提供了兩個專用的方法setProperty()和getProperty()。setProperty()方法用于將配置項的鍵和值添加到Properties集合當中。在第8行代碼中通過調用Properties的propertyNames()方法得到一個包含所有鍵的Enumeration對象,然后在遍歷所有的鍵時,通過調用getProperty()方法獲得鍵所對應的值。
## JDK5.0新特性 - 泛型
#### 為什么使用泛型
通過之前的學習,了解到集合可以存儲任何類型的對象,但是當把一個對象存入集合后,集合會“忘記”這個對象的類型,將該對象從集合中取出時,這個對象的編譯類型就變成了Object類型。換句話說,在程序中無法確定一個集合中的元素到底是什么類型的。那么在取出元素時,如果進行強制類型轉換就很容易出錯。接下來通過一個案例來演示這種情況,如例所示。
```java
import java.util.ArrayList;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 創建ArrayList 集合
list.add("String"); // 添 加字符串對象
list.add("Collection");
list.add(1); // 添加Integer 對象
for (Object obj : list) { // 遍歷集合
String str = (String) obj; // 強制轉換成String 類型
}
}
}
```
運行結果:
```
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Example.main(Example.java:10)
```
例中,向List集合存入了3個元素,分別是兩個字符串和一個整數。在取出這些元素時,都將它們強轉為String類型,由于Integer對象無法轉換為String類型,因此在程序運行時會出現如圖7-33所示的錯誤。為了解決這個問題,在Java中引入了“參數化類型(parameterizedtype)”這個概念,即泛型。它可以限定方法操作的數據類型,在定義集合類時,使用“<參數化類型>”的方式指定該類中方法操作的數據類型,具體格式如下:
```
ArrayList<參數化類型> list = new ArrayList<參數化類型>();
```
接下來對例中的第4行代碼進行修改,如下所示:
```java
ArrayList<String> list = new ArrayList<String>(); //創建集合對象并指定泛型為String
```
程序編譯報錯的原因是修改后的代碼限定了集合元素的數據類型,ArrayList<String>這樣的集合只能存儲String類型的元素,程序在編譯時,編譯器檢查出Integer類型的元素與List集合的規定類型不匹配,編譯不通過,這樣就可以在編譯時解決錯誤,避免程序在運行時發生錯誤。
接下來使用泛型再次對例進行改寫,如例所示。
```java
import java.util.ArrayList;
public class Example {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(); // 創建ArrayList 集合,使用泛型
list.add("String"); // 添加字符串對象
list.add("Collection");
for (String str : list) { // 遍歷集合
System.out.println(str); // 強制轉換成String 類型
}
}
}
```
運行結果:
```
String
Collection
```
例中,使用泛型規定了ArrayList集合只能存入String類型元素。之后向集合中存入了兩個String類型元素,并對這個集合進行遍歷,從運行結果可以看出該例程可以正常運行。需要注意的是,在使用泛型后每次遍歷集合元素時,可以指定元素類型為String,而不是Object,這樣就避免了在程序中進行強制類型轉換。
#### 自定義泛型
上一小節講解了在集合上如何使用泛型,那么在程序中是否能自定義泛型呢? 假設要實現一個簡單的容器,用于緩存程序中的某個值,此時在這個容器類中勢必要定義兩個方法save()和get(),一個用于保存數據,另一個用于取出數據,這兩個方法的定義如下:
```
void save(參數類型參數){……}
返回值參數類型get(){……}
```
為了能存儲任意類型的對象,save()方法的參數需要定義為Object類型,同樣get()方法的返回值也需要是Object類型。但是當使用get()方法取出這個值時,有可能忘記當初存儲的是什么類型的值,在取出時將其轉換為String類型,這樣程序就會發生錯誤。這種錯誤是很麻煩的,為了避免這個問題,就可以使用泛型。
如果在定義一個類CachePool時使用<T>聲明參數類型(T 其實就是Type的縮寫,這里也可以使用其他字符,為了方便理解都定義為T),將save()方法的參數類型和get()方法的返回值類型都聲明為T,那么在存入元素時元素的類型就被限定了,容器中就只能存入這種T 類型的元素,在取出元素時就無須進行類型轉換。接下來通過一個案例來看一下如何自定義泛型,如:
```java
class cachePool<T> { // 在創建類時,聲明參數類型為T
T temp;
public void save(T temp) { // 在創建save()方法時,指定參數類型為T
this.temp = temp;
}
public T get() { // 在創建get()方法時,指定返回值類型為T
return temp;
}
}
public class Example {
public static void main(String[] args) {
// 在實例化對象時,傳入參數為Integer 類型
cachePool<Integer> pool = new cachePool<Integer>();
pool.save(new Integer(1));
Integer temp = pool.get();
System.out.println(temp);
}
}
```
運行結果:
```
1
```
例中,在定義CachePool類時,聲明了參數類型為T,在實例化對象時通過<Integer>將參數T 指定為Integer類型,同時在調用save()方法時傳入的數據也是Integer類型,那么調用get()方法取出的數據自然就是Integer類型,這樣做的好處是不需要進行類型轉換。
## Collections工具類
在程序中,針對集合的操作非常頻繁,例如將集合中的元素排序、從集合中查找某個元素等。針對這些常見操作,JDK 提供了一個工具類專門用來操作集合,這個類就是Collections,它位于java.util包中。Collections類中提供了大量的方法用于對集合中的元素進行排序、查找和修改等操作,接下來對這些常用的方法進行介紹。
- 排序操作
Collections類中提供了一系列方法用于對List集合進行排序,如表所示。

接下來通過一個案例針對表中的方法進行學習,如例所示。
```java
import java.util.ArrayList;
import java.util.Collections;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList();
Collections.addAll(list, "C", "Z", "B", "K"); // 添加元素
System.out.println("排序前: " + list); // 輸出排序前的集合
Collections.reverse(list); // 反轉集合
System.out.println("反轉后: " + list);
Collections.sort(list); // 按自然順序排列
System.out.println("按自然順序排序后: " + list);
Collections.shuffle(list);
System.out.println("洗牌后: " + list);
}
}
```
運行結果:
```
排序前: [C, Z, B, K]
反轉后: [K, B, Z, C]
按自然順序排序后: [B, C, K, Z]
洗牌后: [K, C, Z, B]
```
- 查找、替換操作
Collections類還提供了一些常用方法用于查找、替換集合中的元素,如表所示。

接下來使用表中的方法通過一個案例演示如何查找、替換集合中的元素,如例所示。
```java
import java.util.ArrayList;
import java.util.Collections;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList();
Collections.addAll(list, -3, 2, 9, 5, 8);
System.out.println("集合中的元素: " + list);
System.out.println("集合中的最大元素: " + Collections.max(list));
System.out.println("集合中的最小元素: " + Collections.min(list));
Collections.replaceAll(list, 8, 0); // 將集合中的8 用0 替換掉
System.out.println("替換后的集合: " + list);
}
}
```
運行結果:
```
集合中的元素: [-3, 2, 9, 5, 8]
集合中的最大元素: 9
集合中的最小元素: -3
替換后的集合: [-3, 2, 9, 5, 0]
```
Collections類中還有一些其他方法,有興趣的初學者,可以根據需要自學API幫助文檔,這里就不再介紹了。
## Arrays工具類
- **使用Arrays的sort()方法排序**
在前面學習數組時,要想對數組進行排序就需要自定義一個排序方法,其實也可以使用Arrays工具類中的靜態方法sort()來實現這個功能。接下來通過一個案例來學習sort()方法的使用,如例所示。
```java
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] arr = { 9, 8, 3, 5, 2 }; // 初始化一個數據
System.out.print("排序前: ");
printArray(arr); // 打印原數組
Arrays.sort(arr); // 調用Arrays 的sort 方法排序
System.out.print("排序后: ");
printArray(arr);
}
public static void printArray(int[] arr) { // 定義打印數組方法
System.out.print("[");
for (int x = 0; x < arr.length; x++) {
if (x != arr.length - 1) {
System.out.print(arr[x] + ",");
} else {
System.out.println(arr[x] + "]");
}
}
}
}
```
運行結果:
```
排序前: [9,8,3,5,2]
排序后: [2,3,5,8,9]
```
- **使用Arrays的binarySearch(Object[] a,Object key)方法查找元素**
程序開發中,經常會在數組中查找某些特定的元素,如果數組中元素較多時查找某個元素就會非常煩瑣。為此,Arrays類中還提供了一個方法binarySearch(Object[] a, Object key)用于查找元素,接下來通過一個案例來學習該方法的使用,如例所示。
```java
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] arr = { 9, 8, 3, 5, 2 };
Arrays.sort(arr); // 調用排序方法,對數組排序{2,3,5,8,9}
int index = Arrays.binarySearch(arr, 3); // 查找指定元素3
System.out.println("數組排序后元素3 的索引是:" + index);// 輸出打印元素所在的索引位置
}
}
```
運行結果:
```
數組排序后元素3 的索引是:1
```
從運行結果可以看出,使用Arrays的binarySearch(Object[] a,Object key)方法查找出了3在數組中的索引為1。需要注意的是,binarySearch()方法只能針對排序后的數組進行元素的查找,因為該方法采用的是二分法查找。所謂二分法查找,就是每次將指定元素和數組中間位置的元素進行比較,從而排除掉其中的一半元素,這樣的查找是非常高效的。接下來通過一個圖例來演示二分法查找元素的過程,如圖所示。

圖中的start、end和mid(mid=(start+end)/2)分別代表在數組中查找區間的開始索引、結束索引和中間索引,假設查找的元素為key,接下來分步驟講解元素的查找過程。
第一步,判斷開始索引start和結束索引end,如果start<=end,則key和arr[mid]進行比較;如果兩者相等,說明找到了該元素;如果不相等,則返回元素arr[mid]。
第二步,將key和arr[mid]繼續進行比較,如果key<arr[mid],表示查找的值處于索引start和mid之間,這時執行第三步,否則表示要查找的值處于索引mid和end之間,執行第四步。
第三步,將查找區間的結束索引end置為mid-1,繼續查找,直到start>end,表示查找的數組不存在,這時執行第五步。
第四步,將查找區間的開始索引start置為mid+1,結束索引不變,繼續查找,直到start>end,表示查找的數組不存在,這時執行第五步。
第五步,返回“(插入點)-1”。這個“插入點”指的是大于key值的第一個元素在數組中的位置,如果數組中所有的元素值都小于要查找的對象,“插入點”就等于Arrays.size()。
- **使用Arrays的copyOfRange(int[] original, int from,int to)方法拷貝元素**
在程序開發中,經常需要在不破壞原數組的情況下使用數組中的部分元素,這時可以使用Arrays工具類的copyOfRange(int[] original, int from, int to)方法將數組中指定范圍的元素復制到一個新的數組中,該方法中參數original表示被復制的數組,from 表示被復制元素的初始索引(包括),to表示被復制元素的最后索引(不包括),接下來通過一個案例來學習如何拷貝數組,如例所示。
```java
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] arr = { 9, 8, 3, 5, 2 };
int[] copied = Arrays.copyOfRange(arr, 1, 7);
for (int i = 0; i < copied.length; i++) {
System.out.print(copied[i] + " ");
}
}
}
```
運行結果:
```
8 3 5 2 0 0
```
例中,使用Arrays的copyOfRange(arr,1,7)方法將數組{9,8,3,5,2}中從arr[1](包括開始索引對應的元素)到arr[6](不包括結束索引對應的元素)這6個元素復制到新數組copied中,由于原數組arr的最大索引為4,因此只有arr[1]到arr[4]這四個元素8,3,5,2復制到了新數組copied中,另外兩個元素放入了默認值0。
- **使用Arrays的fill(Object[] a,Object val)方法填充元素**
程序開發中,經常需要用一個值替換數組中的所有元素,這時可以使用Array的fill(Object[] a,Object val)方法,該方法可以將指定的值賦給數組中的每一個元素,接下來通過一個案例來演示如何填充元素,如例所示。
```java
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 };
Arrays.fill(arr, 8); // 用8 替換數組中的每一個值
for (int i = 0; i < arr.length; i++) {
System.out.println(i + ": " + arr[i]);
}
}
}
```
運行結果:
```
0: 8
1: 8
2: 8
3: 8
```
- **使用Arrays的toString(int[] arr)方法把數組轉換為字符串**
在程序開發中,經常需要把數組以字符串的形式輸出,這時就可以使用Arrays工具類的另一個方法toString(int[] arr)。需要注意的是,該方法并不是對Object類toString()方法的重寫,只是用于返回指定數組的字符串形式。接下來通過一個案例來演示如何將數組轉換為字符串,如例所示。
```java
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] arr = { 9, 8, 3, 5, 2 };
String arrString = Arrays.toString(arr); // 使用toString()方法將數組轉換為字符串
System.out.println(arrString);
}
}
```
運行結果:
```
[9, 8, 3, 5, 2]