1.Java集合概述
在編程中,常常需要集中存放多個數據。當然我們可以使用數組來保存多個對象。但數組長度不可變化,一旦在初始化時指定了數組長度,則這個數組長度是不可變的,如果需要保存個數變化的數據,數組就顯得無能為力了;而且數組無法保存具有映射關系的數據,如成績表:語文-80,數學-90,這種數據看上去像兩個數組,但這兩個數組的元素之間有一定的關聯關系。
為了保存數量不確定的數據,以及保存具有映射關系的數據,Java提供了集合類。集合類主要負責保存、盛裝其他數據,因此集合類也被稱為容器類。所有集合類都位於Java.util包下。
集合類和數組不一樣,數組既可以是基本類型的值,也可以是對象(實際上保存的是對象的引用變量);而集合里只能保存對象,例如不能保存int、只能保存integer。
Java的集合類主要有兩個接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,這兩個接口又包含了一些子接口和實現類
如下圖所示Collection體系的繼承樹:
如上圖所示顯示了Collection體系里的集合,Set、List接口是Collection接口派生的兩個子接口,它們分別代表了無序集合、有序集合;Queue[kju:]是Java提供的隊列(隊列特點:先進先出)實現,有點類似於List。
如下圖所示Map體系的繼承樹:
如上圖所示顯示了Map接口的眾多實現類,這些實現類在功能、用法上存在一定的差異,但他們都有一個功能特征:Map保存的每項數據都是key-value對,也就是由key和value兩個值組成。就向前 面簡紹的成績單:語文-80,數學-90,每項成績都是由2個值組成:科目名和成績;Map里的key是不可重復的,key用於標識集合里每項數據,如果需要查詢Map中的數據時,總是根據Map的key來獲取。
根據上兩圖,我們可以把Java的所有集合分成三大類,其中Set集合類似於一個罐子,把一個對象添加到Set集合時,Set集合無法記住添加這個元素的順序,所以Set里的元素不能重復(否則系統無法准確識別這個元素);List集合非常像一個數組,它可以記住每次添加元素的順序,只是List的長度可變。Map集合也像一個罐子,只是它里面的每項數據都由兩個值組成。
如下圖所示顯示了三種集合的示意圖:
從上圖可以看出,如果訪問List集合中的元素,可以直接根據元素的索引來訪問;如果需要訪問Map集合中的元素,可以根據每項元素的key來訪問其value;如果希望訪問Set集合中的元素,則只能根據元素本身來訪問(這也是Set集合里的元素不容許重復的原因)。
對於Set、List和Map三種集合,最常用的實現類分別為HashSet、ArrayList和HashMap,這三個實現類都沒有實現並發控制,是屬於線程不安全的。
2.Collection和Iterator接口
-
Collection接口
Collection接口是List、Set和Queue接口的父接口,該接口中定義的方法既可用於操作Set集合,也可用於操作List和Queue集合。Collection中定義了如下操作集合元素的方法:
-
- boolean add(Object o); 該方法用於向集合里添加一個元素。如果集合對象被添加操作改變了則返回true。
- boolean addAll(Collection c); 該方法把集合c里面的所有元素添加到指定集合里。如果集合對象唄添加操作改變了則返回true。
- void clear(); 清楚集合里的所有元素,將集合長度變為0。
- boolean contains(Object o); 返回集合里是否包含指定元素。
- boolean containsAll(Collection c); 返回集合里是否包含集合c里面的所有元素。
- boolean isEmpty(); 返回集合是否為空。當集合長度為0時,返回true,否則返回false。
- Iterator iterator(); 返回一個Iterator對象,用於遍歷集合里的元素。
- boolean remove(Object o); 刪除集合中指定元素o,當集合中包含一個或多個元素o時,這些元素將被刪除,該方法將返回true。
- boolean removeAll(Collection c); 從集合中刪除集合c里包含的所有元素(相當於調用該方法的集合減集合c),如果刪除了一個或一個以上的元素,該方法返回true。
- boolean retainAll(Collection c); 從集合中刪除集合c里不包含的所有元素(相當於取得把調用該方法的集合變成該集合和集合c的交集),如果該操作改變了調用該方法的集合,該方法返回true。
- int size(); 該方法返回集合元素里的個數。
- Object[] toArray(); 該方法把集合轉換成一個數組,所有集合元素變成對應的數組元素。
-
Iterator接口
Iterator接口也是Java集合框架的成員,但它與Collection系列、Map系列的集合不一樣:Collection系列集合、Map系列集合主要用於盛裝其他對象,而Iterator則主要用於遍歷(即迭代訪問)Collection集合中的元素,Iterator對象也被稱為迭代器。
Iterator接口隱藏了各種Collection實現類的底層細節,向應用程序提供了遍歷Collection集合元素的統一編程接口,Iterator接口里定義了如下三個方法:
-
- boolean hasNext(); 如果被迭代的集合元素還沒有被遍歷,則返回true。
- Object next(); 返回集合里下一個元素。
- void remove(); 刪除集合里上一次next返回的元素。
class test{ public static void main(String[] args){ Collection books = new HashSet(); books.add("語文"); books.add("數學"); books.add("英語"); //打印結果為[語文, 英語, 數學] System.out.println(books); Iterator it = books.iterator(); while(it.hasNext()){ String book= (String)it.next(); if("語文".equals(book)){ it.remove(); } //對book變量賦值,不會改變集合元素本身 book = "測試字段"; } //打印結果為[英語, 數學] System.out.println(books); } }
上面代碼對迭代變量book進行賦值,但當我們再次輸出books集合時,看到集合里的元素沒有任何的改變。這就可以得出一個結論:當使用Iterator對集合元素進行迭代時,Iterator並不是把集合元素本身傳給了迭代變量,二是把集合元素的值傳給了迭代變量,所以修改迭代變量的值對集合本身沒有任何改變。如下原理:
public class test{ public static void main(String[] args){ String str = "zhangsan"; //打印結果為zhangsan System.out.println(str); updateString(str); //打印結果還是zhangsan System.out.println(str); } public static void updateString(String str2){ //這個就相當於你在Iterator里面傳的值,所以修改變量str2的值對str本身沒有任何變化 str2 = "lisi"; } }
但是當傳遞的是對象時,則可以修改傳入對象的值如下:
public class test{ public static void main(String[] args){ Person p = new Person(); p.setId(1); p.setName("zhangsan"); //打印結果為id = 1 name = zhangsan System.out.println(p); updatePerson(p); //打印結果為id = 2 name = lisi System.out.println(p); } public static void updatePerson(Person p1){ //這個就相當於你在Iterator里面傳的是對象,它給你傳的是地址,所以你修改p1,就相當於修改了p的值 p1.setId(2); p1.setName("lisi"); } } class Person{ private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "id = " + id + " name = " + name; } }
當使用Iterator來迭代訪問Collection集合元素時,Collection集合里的元素不能被改變,只有通過Iterator的remove方法來刪除上一次next方法返回的集合元素才可以。否則將會引發Java.util.ConcurrentModificationException異常。如下:
public class Test { public static void main(String[] args){ Collection books = new HashSet(); books.add("語文"); books.add("數學"); books.add("英語"); Iterator it = books.iterator(); while(it.hasNext()){ String book = (String)it.next(); if(book.equals("英語")){ //這樣做就會拋Java.util.ConcurrentModificationException books.remove(book); } } } }
-
使用foreach循環遍歷集合元素
除了可以使用Iterator類迭代訪問Collection集合里的元素外,也可以使用foreach循環來迭代訪問集合元素,而且更加便捷如下:
public class Test { public static void main(String[] args){ Collection books = new HashSet(); books.add("語文"); books.add("數學"); books.add("英語"); for(Object o : books){ String book = (String)o; if(book.equals("語文")){ //這行代碼將會引發java.util.ConcurrentModificationException異常 //books.remove(book); } System.out.println(o); } } }
如上所示,同樣,當使用foreach循環迭代訪問集合元素時,該集合也不能被改變,否則將引發ConcurrentModificationException異常。