- Java高級特性編程及實戰
- 肖睿 龍浩 孫琳
- 6186字
- 2020-09-18 18:35:52
任務1 查詢標題
關鍵步驟如下。
- 創建集合對象,并添加數據。
- 統計新聞標題總數量。
- 輸出新聞標題名稱。
1.1.1 認識集合
開發應用程序時,如果想存儲多個同類型的數據,可以使用數組來實現;但是使用數組存在如下一些明顯缺陷:
- 數組長度固定不變,不能很好地適應元素數量動態變化的情況。
- 可通過數組名.length獲取數組的長度,卻無法直接獲取數組中實際存儲的元素個數。
- 數組采用在內存中分配連續空間的存儲方式存儲,根據元素信息查找時效率比較低,需要多次比較。
從以上分析可以看出數組在處理一些問題時存在明顯的缺陷,針對數組的缺陷,Java提供了比數組更靈活、更實用的集合框架,可大大提高軟件的開發效率,并且不同的集合可適用于不同應用場合。
Java集合框架提供了一套性能優良、使用方便的接口和類,它們都位于java.util包中,其主要內容及彼此之間的關系如圖1.1所示。

圖1.1 Java集合框架圖
從圖1.1中可以看出,Java的集合類主要由Map接口和Collection接口派生而來,其中Collection接口有兩個常用的子接口,即List接口和Set接口,所以通常說Java集合框架由3大類接口構成(Map接口、List接口和Set接口)。本章講解的主要內容就是圍繞這3大類接口進行的。
注意
虛線框表示接口或者抽象類,實線框表示開發中常用的實現類。
1.1.2 List接口
Collection接口是最基本的集合接口,可以存儲一組不唯一、無序的對象。List接口繼承自Collection接口,是有序集合。用戶可使用索引訪問List接口中的元素,類似于數組。List接口中允許存放重復元素,也就是說List可以存儲一組不唯一、有序的對象。
List接口常用的實現類有ArrayList和LinkedList。
1. 使用ArrayList類動態存儲數據
針對數組的一些缺陷,Java集合框架提供了ArrayList集合類,對數組進行了封裝,實現了長度可變的數組,而且和數組采用相同的存儲方式,在內存中分配連續的空間,如圖1.2所示,所以,經常稱ArrayList為動態數組。但是它不等同于數組,ArrayList集合中可以添加任何類型的數據,并且添加的數據都將轉換成Object類型,而在數組中只能添加同一數據類型的數據。

圖1.2 ArrayList存儲方式示意圖
ArrayList類提供了很多方法用于操作數據,如表1-1中列出的是ArrayList類的常用方法。
表1-1 ArrayList類的常用方法

示例1
使用ArrayList常用方法動態操作數據。
實現步驟如下。
(1)導入ArrayList類。
(2)創建ArrayList對象,并添加數據。
(3)判斷集合中是否包含某元素。
(4)移除索引為0的元素。
(5)把索引為1的元素替換為其他元素。
(6)輸出某個元素所在的索引位置。
(7)清空ArrayList集合中的數據。
(8)判斷ArrayList集合中是否包含數據。
關鍵代碼:

在示例1中,① 的代碼調用ArrayList的無參構造方法,創建集合對象。常用的ArrayList類的構造方法還有一個帶參數的重載版本,即ArrayList(int initialCapacity),它構造一個具有指定初始容量的空列表。
② 的代碼將list集合中索引為0的元素刪除,list集合的下標是從0開始,也就是刪除了“張三豐”,集合中現有元素為“郭靖”和“楊過”。
③ 的代碼將list集合中索引為1的元素替換為“黃蓉”,即將“楊過”替換為“黃蓉”,集合中現有元素為“郭靖”和“黃蓉”。
④ 的代碼是使用for循環遍歷集合,輸出集合中的所有元素。list.get(i)取出集合中索引為i的元素,并強制轉換為String類型。
⑤ 的代碼為輸出元素“小龍女”所在的索引位置,因集合中沒有該元素,所以輸出結果為-1。
⑥ 的代碼是使用增強for循環遍歷集合,輸出集合中的所有元素。增強for循環的語法在Java基礎課程中講過,這里不再贅述。可以看出,遍歷集合時使用增強for循環比普通for循環在寫法上更加簡單方便,而且不用考慮下標越界的問題。
⑦ 的代碼用來判斷list集合是否為空,因為前面執行了list.clear()操作,所以集合已經為空,輸出為true。
注意
① 調用ArrayList類的add(Object obj)方法時,添加到集合當中的數據將被轉換為Object類型。
② 使用ArrayList類之前,需要導入相應的接口和類,代碼如下:
import java.util.ArrayList; import java.util.List;
示例2
使用ArrayList集合存儲新聞標題信息(包含ID、名稱、創建者),輸出新聞標題的總數量及每條新聞標題的名稱。
實現步驟如下。
(1)創建ArrayList對象,并添加數據。
(2)獲取新聞標題的總數。
(3)遍歷集合對象,輸出新聞標題名稱。
關鍵代碼:
//創建新聞標題對象,NewTitle為新聞標題類 NewTitle car=new NewTitle(1, "汽車", "管理員"); NewTitle test=new NewTitle(2, "高考", "管理員"); //創建存儲新聞標題的集合對象 List newsTitleList=new ArrayList(); //按照順序依次添加新聞標題 newsTitleList.add(car); newsTitleList.add(test); //獲取新聞標題的總數 System.out.println("新聞標題數目為:"+newsTitleList.size()+"條"); //遍歷集合對象 System.out.println("新聞標題名稱為:"); for(Object obj:newsTitleList){ NewTitle title=(NewTitle)obj; System.out.println(title.getTitleName()); }
輸出結果如圖1.3所示。

圖1.3 輸出新聞標題信息
在示例2中,ArrayList集合中存儲的是新聞標題對象。在ArrayList集合中可以存儲任何類型的對象。其中,代碼List newsTitleList=new ArrayList();是將接口List的引用指向實現類ArrayList的對象。在編程中將接口的引用指向實現類的對象是Java實現多態的一種形式,也是軟件開發中實現低耦合的方式之一,這樣的用法可以大大提高程序的靈活性。隨著編程經驗的積累,開發者對這個用法的理解會逐步加深。
ArrayList集合因為可以使用索引來直接獲取元素,所以其優點是遍歷元素和隨機訪問元素的效率比較高。但是由于ArrayList集合采用了和數組相同的存儲方式,在內存中分配連續的空間,因此在添加和刪除非尾部元素時會導致后面所有元素的移動,這就造成在插入、刪除等操作頻繁的應用場景下使用ArrayList會導致性能低下。所以數據操作頻繁時,最好使用LinkedList存儲數據。
2. 使用LinkedList類動態存儲數據
LinkedList類是List接口的鏈接列表實現類。它支持實現所有List接口可選的列表的操作,并且允許元素值是任何數據,包括null。
LinkedList類采用鏈表存儲方式存儲數據,如圖1.4所示,優點在于插入、刪除元素時效率比較高,但是LinkedList類的查找效率很低。

圖1.4 LinkedList類存儲示意圖
它除了包含ArrayList類所包含的方法外,還提供了表1-2所示的一些方法,可以在LinkedList類的首部或尾部進行插入、刪除操作。
表1-2 LinkedList類的常用方法

示例3
使用LinkedList集合存儲新聞標題(包含ID、名稱、創建者),實現獲取、添加及刪除頭條和末條新聞標題信息功能,并遍歷集合。
實現步驟如下。
(1)創建LinkedList對象,并添加數據。
(2)添加頭條和末條新聞標題。
(3)獲取頭條和末條新聞標題信息。
(4)刪除頭條和末條新聞標題。
關鍵代碼:
//創建多個新聞標題對象 NewTitle car=new NewTitle(1, "汽車", "管理員"); NewTitle medical=new NewTitle(2, "醫學", "管理員"); NewTitle fun=new NewTitle(3, "娛樂", "管理員"); NewTitle gym=new NewTitle(4, "體育", "管理員"); //創建存儲新聞標題的集合對象并添加數據 LinkedList newsTitleList=new LinkedList(); newsTitleList.add(car); newsTitleList.add(medical); //添加頭條新聞標題和末條新聞標題 newsTitleList.addFirst(fun); newsTitleList.addLast(gym); System.out.println("頭條和末條新聞已添加"); //獲取頭條以及最末條新聞標題 NewTitle first=(NewTitle) newsTitleList.getFirst(); System.out.println("頭條的新聞標題為:"+first.getTitleName()); NewTitle last=(NewTitle) newsTitleList.getLast(); System.out.println("排在最后的新聞標題為:"+last.getTitleName()); //刪除頭條和末條新聞標題 newsTitleList.removeFirst(); newsTitleList.removeLast(); System.out.println("頭條和末條新聞已刪除"); System.out.println("遍歷所有新聞標題:"); for(Object obj:newsTitleList){ NewTitle newTitle=(NewTitle)obj; System.out.println("新聞標題名稱:"+newTitle.getTitleName()); }
輸出結果如圖1.5所示。

圖1.5 使用LinkedList存儲并操作新聞標題信息
除了表1-2中列出的LinkedList類提供的方法外,LinkedList類和ArrayList類所包含的大部分方法是完全一樣的,這主要是因為它們都是List接口的實現類。由于ArrayList采用和數組一樣的連續的順序存儲方式,當對數據頻繁檢索時效率較高,而LinkedList類采用鏈表存儲方式,當對數據添加、刪除或修改比較多時,建議選擇LinkedList類存儲數據。
1.1.3 Set接口
1. Set接口概述
Set接口是Collection接口的另外一個常用子接口,Set接口描述的是一種比較簡單的集合。集合中的對象并不按特定的方式排序,并且不能保存重復的對象,也就是說Set接口可以存儲一組唯一、無序的對象。
Set接口常用的實現類有HashSet。
2. 使用HashSet類動態存儲數據
假如現在需要在很多數據中查找某個數據,LinkedList類就無需考慮了,它的數據結構決定了它的查找效率低下。如果使用ArrayList類,在不知道數據的索引且需要全部遍歷的情況下,效率一樣很低下。為此Java集合框架提供了一個查找效率高的集合類HashSet。HashSet類實現了Set接口,是使用Set集合時最常用的一個實現類。HashSet集合的特點如下。
- 集合內的元素是無序排列的。
- HashSet類是非線程安全的。
- 允許集合元素值為null。
表1-3中列舉了HashSet類的常用方法。
表1-3 HashSet類的常用方法

示例4
使用HashSet類的常用方法存儲并操作新聞標題信息,并遍歷集合。
實現步驟如下。
(1)創建HashSet對象,并添加數據。
(2)獲取新聞標題的總數。
(3)判斷集合中是否包含汽車新聞標題。
(4)移除對象。
(5)判斷集合是否為空。
(6)遍歷集合。
關鍵代碼:
//創建多個新聞標題對象 NewTitle car=new NewTitle(1, "汽車", "管理員"); NewTitle test=new NewTitle(2, "高考", "管理員"); //創建存儲新聞標題的集合對象 Set newsTitleList=new HashSet(); //按照順序依次添加新聞標題 newsTitleList.add(car); newsTitleList.add(test); //獲取新聞標題的總數 System.out.println("新聞標題數目為:"+newsTitleList.size()+"條"); //判斷集合中是否包含汽車新聞標題 System.out.println("汽車新聞是否存在:"+newsTitleList.contains(car)); //輸出true newsTitleList.remove(test); //移除對象 System.out.println("汽車對象已刪除"); System.out.println("集合是否為空:"+newsTitleList.isEmpty()); //判斷是否為空 //遍歷所有新聞標題 System.out.println("遍歷所有新聞標題:"); for(Object obj:newsTitleList){ NewTitle title=(NewTitle)obj; System.out.println(title.getTitleName()); }
輸出結果如圖1.6所示。

圖1.6 使用HashSet類存儲并操作新聞標題信息
注意
使用HashSet類之前,需要導入相應的接口和類,代碼如下:
import java.util.Set; import java.util.HashSet;
在示例4中,通過增強for循環遍歷HashSet,前面講過List接口可以使用for循環和增強for循環兩種方式遍歷。使用for循環遍歷時,通過get()方法取出每個對象,但HashSet類不存在get()方法,所以Set接口無法使用普通for循環遍歷。其實遍歷集合還有一種比較常用的方式,即使用Iterator接口。
1.1.4 Iterator接口
1. Iterator接口概述
Iterator接口表示對集合進行迭代的迭代器。Iterator接口為集合而生,專門實現集合的遍歷。此接口主要有如下兩個方法:
- hasNext():判斷是否存在下一個可訪問的元素,如果仍有元素可以迭代,則返回true。
- next():返回要訪問的下一個元素。
凡是由Collection接口派生而來的接口或者類,都實現了iterate()方法,iterate()方法返回一個Iterator對象。
2. 使用Iterator遍歷集合
下面通過示例來學習使用迭代器Iterator遍歷Arraylist集合。
示例5
使用Iterator接口遍歷ArrayList集合。
實現步驟如下。
(1)導入Iterator接口。
(2)使用集合的iterate()方法返回Iterator對象。
(3)while循環遍歷。
(4)使用Iterator的hasNext()方法判斷是否存在下一個可訪問的元素。
(5)使用Iterator的next()方法返回要訪問的下一個元素。
關鍵代碼:
public static void main(String[] args){ ArrayList list=new ArrayList(); list.add("張三"); list.add("李四"); list.add("王五"); list.add(2, "杰倫"); System.out.println("使用Iterator遍歷,分別是:"); Iterator it=list.iterator(); //獲取集合迭代器Iterator while(it.hasNext()){ //通過迭代器依次輸出集合中所有元素的信息 String name=(String)it.next(); System.out.println(name); } }
輸出結果:
使用Iterator遍歷,分別是: 張三 李四 杰倫 王五
示例5中是以ArrayList為例使用Iterator接口,其他由Collection接口直接或間接派生的集合類,如已經學習的LinkedList、HashSet等,同樣可以使用Iterator接口進行遍歷,遍歷方式與示例5遍歷ArrayList集合的方式相同。例如,將示例5改為使用Iterator對象遍歷,關鍵代碼如下。
//使用Iterator遍歷HashSet集合 while(iterator.hasNext()){ NewTitle title=(NewTitle) iterator.next(); System.out.println(title.getTitleName()); }
1.1.5 Map接口
1. Map接口概述
Map接口存儲一組成對的鍵(key)——值(value)對象,提供key到value的映射,通過key來檢索。Map接口中的key不要求有序,不允許重復。value同樣不要求有序,但允許重復。表1-4中列舉了Map接口的常用方法。
表1-4 Map接口的常用方法

Map接口中存儲的數據都是鍵——值對,例如,一個身份證號碼對應一個人,其中身份證號碼就是key,與此號碼對應的人就是value。
2. 使用HashMap類動態存儲數據
最常用的Map實現類是HashMap,其優點是查詢指定元素效率高。
示例6
使用HashMap類存儲學生信息,要求可以根據英文名檢索學生信息。
實現步驟如下。
(1)導入HashMap類。
(2)創建HashMap對象。
(3)調用HashMap對象的put()方法,向集合中添加數據。
(4)輸出學員個數。
(5)輸出鍵集。
(6)判斷是否存在“Jack”這個鍵,如果存在,則根據鍵獲取相應的值。
(7)判斷是否存在“Rose”這個鍵,如果存在,則根據鍵獲取相應的值。
關鍵代碼:
//創建學員對象 Student student1=new Student("李明", "男"); Student student2=new Student("劉麗", "女"); //創建保存“鍵——值對”的集合對象 Map students=new HashMap(); //把英文名稱與學員對象按照“鍵——值對”的方式存儲在HashMap中 students.put("Jack", student1); students.put("Rose", student2); //輸出學員個數 System.out.println("已添加"+students.size()+"個學員信息"); //輸出鍵集 System.out.println("鍵集:"+students.keySet()); String key="Jack"; //判斷是否存在“Jack”這個鍵,如果存在,則根據鍵獲取相應的值 if(students.containsKey(key)){ Student student=(Student)students.get(key); System.out.println("英文名為"+key+"的學員姓名:"+student.getName()); } String key1="Rose"; //判斷是否存在“Rose”這個鍵,如果存在,則刪除此鍵——值對 if(students.containsKey(key1)){ students.remove(key1); System.out.println("學員"+key1+"的信息已刪除"); }
輸出結果如圖1.7所示。

圖1.7 使用HashMap類存儲并檢索學生信息
注意
① 數據添加到HashMap集合后,所有數據的數據類型將轉換為Object類型,所以從其中獲取數據時需要進行強制類型轉換。
② HashMap類不保證映射的順序,特別是不保證順序恒久不變。
遍歷HashMap集合時可以遍歷鍵集和值集。
示例7
改進示例6,遍歷所有學員的英文名及學員詳細信息。
實現步驟如下。
(1)遍歷鍵集。
(2)遍歷值集。
關鍵代碼:
//創建學員對象 Student student1=new Student("李明", "男"); Student student2=new Student("劉麗", "女"); //創建保存“鍵——值對”的集合對象 Map students=new HashMap(); //把英文名稱與學員對象按照“鍵——值對”的方式存儲在HashMap中 students.put("Jack", student1); students.put("Rose", student2); //輸出英文名 System.out.println("學生英文名:"); for(Object key:students.keySet()){ System.out.println(key.toString()); } //輸出學生詳細信息 System.out.println("學生詳細信息:"); for(Object value:students.values()){ Student student=(Student)value; System.out.println("姓名:"+student.getName()+",性別:"+student.getSex()); }
輸出結果如圖1.8所示。

圖1.8 使用HashMap集合遍歷學生英文名及詳細信息
在示例7中,使用增強for循環遍歷HashMap集合的鍵集和值集,當然也可以使用前面的普通for循環或者迭代器Iterator來遍歷,視個人習慣而選擇。

Map補充案例
1.1.6 Collections類
Collections類是Java提供的一個集合操作工具類,它包含了大量的靜態方法,用于實現對集合元素的排序、查找和替換等操作。
注意
Collections和Collection是不同的,前者是集合的操作類,后者是集合接口。
1. 對集合元素排序與查找
排序是針對集合的一個常見需求。要排序就要知道兩個元素哪個大哪個小。在Java中,如果想實現一個類的對象之間比較大小,那么這個類就要實現Comparable接口。此接口強行對實現它的每個類的對象進行整體排序。這種排序被稱為類的自然排序,類的compareTo()方法被稱為它的自然比較方法。此方法用于比較此對象與指定對象的順序,如果該對象小于、等于或大于指定對象,則分別返回負整數、零或正整數。
compareTo()方法的定義語法格式如下。
int compareTo(Object obj);
其中:
- 參數:obj即要比較的對象;
- 返回值:負整數、零或正整數,根據此對象是小于、等于還是大于指定對象返回不同的值。
實現此接口的對象列表(和數組)可以通過Collections.sort()方法(和Arrays.sort()方法)進行自動排序。示例8通過實現Comparable接口對集合進行排序。
示例8
學生類Student實現了Comparable接口,重寫了compareTo()方法,通過比較學號實現對象之間的大小比較。
實現步驟如下。
(1)創建Student類。
(2)添加屬性學號number(int)、姓名name(String)和性別gender(String)。
(3)實現Comparable接口、compareTo()方法。
關鍵代碼:
public class Student implements Comparable{ private int number=0; //學號 private String name=""; //姓名 private String gender=""; //性別 public int getNumber(){ return number; } public void setNumber(int number){ this.number=number; } public String getName(){ return name; } public void setName(String name){ this.name=name; } public String getGender(){ return gender; } public void setGender(String gender){ this.gender=gender; } public int compareTo(Object obj){ Student student=(Student)obj; //如果學號相同,那么兩者就是相等的 if(this.number==student.number){ return 0; //如果這個學生的學號大于傳入學生的學號 }else if(this.number>student.getNumber()){ return 1; //如果這個學生的學號小于傳入學生的學號 }else{ return -1; } } }
元素之間可以比較大小之后,就可以使用Collections類的sort()方法對元素進行排序操作了。前面介紹過List接口和Map接口,Map接口本身是無序的,所以不能對Map接口做排序操作;但是List接口是有序的,所以可以對List接口進行排序。注意List接口中存放的元素,必須是實現了Comparable接口的元素才可以。
示例9
使用Collections類的靜態方法sort()和binarySearch()對List集合進行排序與查找。
實現步驟如下。
(1)導入相關類。
(2)初始化數據。
(3)遍歷排序前集合并輸出。
(4)使用Collections類的sort()方法排序。
(5)遍歷排序后集合并輸出。
(6)查找排序后某元素的索引。
關鍵代碼:
//省略聲明Student對象代碼 public static void main(String[] args){ Student student1=new Student(); student1.setNumber(5); Student student2=new Student(); student2.setNumber(2); Student student3=new Student(); student3.setNumber(1); Student student4=new Student(); student4.setNumber(4); ArrayList list=new ArrayList(); list.add(student1); list.add(student2); list.add(student3); list.add(student4); System.out.println("-------排序前-------"); Iterator iterator=list.iterator(); while(iterator.hasNext()){ Student stu=(Student)iterator.next(); System.out.println(stu.getNumber()); } //使用Collections類的sort()方法對List集合進行排序 System.out.println("-------排序后-------"); Collections.sort(list); iterator=list.iterator(); while(iterator.hasNext()){ Student stu=(Student)iterator.next(); System.out.println(stu.getNumber()); } //使用Collections類的binarySearch()方法對List集合進行查找 int index=Collections.binarySearch(list,student3); //① System.out.println("student3的索引是:"+index); }
輸出結果:
-------排序前------- 5 2 1 4 -------排序后------- 1 2 4 5 student3的索引是:0
示例9中,①的代碼是使用Collections類的binarySearch()方法對List集合進行查找,因為student3的學號為1,故排序后索引變為0。
2. 替換集合元素
若有一個需求,需要把一個List集合中的所有元素都替換為相同的元素,則可以使用Collections類的靜態方法fill()來實現。下面通過一個示例來學習使用fill()方法替換元素。
示例10
使用Collections類的靜態方法fill()替換List集合中的所有元素為相同的元素。
實現步驟如下。
(1)導入相關類,初始化數據。
(2)使用Collections類的fill()方法替換集合中的元素。
(3)遍歷輸出替換后的集合。
關鍵代碼:
public static void main(String[] args){ ArrayList list=new ArrayList(); list.add("張三豐"); list.add("楊過"); list.add("郭靖"); Collections.fill(list, "東方不敗"); //替換元素 Iterator iterator=list.iterator(); while(iterator.hasNext()){ String name=(String)iterator.next(); System.out.println(name); } }
輸出結果:
東方不敗 東方不敗 東方不敗
至此,任務1已經全部完成。
- Web應用系統開發實踐(C#)
- Raspberry Pi for Secret Agents(Third Edition)
- 數據結構習題精解(C語言實現+微課視頻)
- 量化金融R語言高級教程
- Visual FoxPro程序設計習題集及實驗指導(第四版)
- Maker基地嘉年華:玩轉樂動魔盒學Scratch
- Learning JavaScript Data Structures and Algorithms(Second Edition)
- Practical Microservices
- Emotional Intelligence for IT Professionals
- UX Design for Mobile
- Appcelerator Titanium:Patterns and Best Practices
- Apache Solr for Indexing Data
- Building Apple Watch Projects
- IBM DB2 9.7 Advanced Application Developer Cookbook
- Learning Zimbra Server Essentials