第十一章:持有對象(容器類源碼非常清楚也很強大,像我們這種菜鳥應該多學習)
- 泛型和集合類型安全
- 向上轉型可以適用
- 容器類:
- Collection:一個獨立元素的序列,這些元素都服從一條或者多條規則
- list:按照插入的順序保存元素,不關心是否重複
- set:不能有重複元素
- Queue:按照隊列規則來確定對象產生的順序
- Map:鍵值對
- Collection:一個獨立元素的序列,這些元素都服從一條或者多條規則
- Arrays.asList():將一個數組當作list,但是底層然是以數組來實現的,所以當對這個List做增刪操作時有可能引起改變數組大小
- 顯示類型參數聲明:List<T> t = Arrays.<T>asList();底層返回的是一個內部類ArrayList
- 容器的打印:
- Collection打印出來的是方括號括住,每個逗號分隔的
- Map則用大括號,鍵與值則是用等號聯繫
- ArrayList和LinkedList都是List類型,都是按照被插入的順序保存元素
- HashSet,TreeSet,LinkedHashSet都是Set類型,是去重複的
- hashSet的存儲順序非常複雜,待後面說
- TreeSet:按照比較結果的升序存儲對象
- LinkedHashSet:按照添加的順序保存對象
- HashMap,TreeMap,LinkedHashMap,存儲的順序和上面一致,其實Set底層就是基於Map來實現的,後面會細說
- List:將元素維護在特定的序列中,在Collection的接口上添加了大量方法
- ArrayList:易於隨機訪問,畢竟是依賴數組實現的
- subList():這個函數所產生的列表的幕後就是初始列表,所以對返回的列表的操作會反映到初始列表中
- 像remove(),retainAll()[表示取交集]等函數背後比對使用的都是equals函數,要注意這個函數比對的是引用,除非覆寫,比如String類中
- LinkedList:易於插入刪除,是依賴鏈表實現的,這個類中多了很多函數,是爲了實現棧和隊列提供基礎的
- 雙向鏈表實現
- 是無頭節點的雙向鏈表
- Queue實在LinkedList基礎之上添加了element()/offer()/peek()/poll()/remove()等方法實現的
- element()/getFirst()/peek()都是取第一個對象,但是前兩者在list爲空時拋出NoSuchElementException異常,peek則返回null
- remove/removeFirst/pull()和上面類似,不過都是取移除並返回頭
- add/addFirst()/addLast()
- ArrayList:易於隨機訪問,畢竟是依賴數組實現的
- 迭代器
- Iterator:單向的
- 使用方法iterator()要求容器返回一個Iterator,Iterator將準備好返回序列的第一個元素
- 使用next()獲得序列的下一個元素
- 使用hasNext()檢查序列中是否有元素
- 使用remove將迭代器新近返回的元素刪除(一般要先調用next(),保證有新近返回的元素,因爲根據源碼知道,remove裏刪除的index是在next()方法裏設置的)
- ListIterator:只能用於List類的訪問,而且是雙向的
- 可以通過調用listIterator()方法產生一個指向List開始處的ListIterator,並且還可以通過調用listIterator(n)創建一個一開始就指向列表索引爲n的元素的ListIterator
- 通過源代碼知道ListIterator是對Iterator的一次擴展,增加了實現雙向移動的一些函數
- Iterator:單向的
- 棧
- 依賴LinkedList實現棧,類名後面的<T>告訴編譯器這將是一個參數化類型
import java.util.LinkedList; public class StackByLinkedList<T> { private LinkedList<T> storage = new LinkedList<T>(); public void push(T v){ storage.push(v); } public T peek(){ return storage.peek(); } public T pop(){ return storage.pop(); } public boolean empty(){ return storage.isEmpty(); } public String toString(){ return storage.toString(); } }
- 當然在util包中實現了Stack這個類,不過是繼承了Vector,依賴數組實現的
- Set:之前說過Set是依賴Map的key來實現的,可以看源碼
- 不保存重複的元素,一般利用這個特性來測試歸屬行
- Set和Collection完全一樣的接口,沒有任何額外的功能
- HashSet/TreeSet/LinkedHashSet
- TreeSet依賴紅黑樹,結果是排序的
- HashSet依賴散列函數
- LinkedHashSet也使用了散列函數,但是使用了鏈表來維護元素的插入順序
- Map:鍵值對,可以用於組合別的數據結構,如Map<A,List<? extends B>>();
- Queue:典型的FIFO
- LinkedLIst提供了方法支持隊列的行爲,而且實現了Queue的接口
- offer():將一個元素插入到隊尾
- peek/element都是在不移除的情況下返回對頭
- poll/remove都是在移除的情況下返回對頭
- PriorityQueue
- 一般情況下會維持一個堆,在offer()插入時會根據一定的規則排序,可以通過Comparator來修改排序規則
- 一般情況下重複是允許的,而且最小的值擁有最高的優先級
- Collection與iterator
- Foreach與迭代器
- foreach適用於所有Collection
- 所有實現了Iterable的接口都可以用foreach實現移動
public class IterableClass implements Iterable<String>{ private String[] words = {"s0","s1","s2","s3","s4"}; public Iterator<String> iterator(){ return new Iterator<String>() { private int index = 0; public String next(){ return words[index++]; } public boolean hasNext(){ return index < words.length; } public void remove(){ } }; } @Test public void test(){ for(String s : new IterableClass()){ System.out.println(s+" "); } } }
- 所有的Collection(除了Map)都實現了Iterable接口,map可以使用Entry來構建成Set集合
import java.util.Map; import org.junit.Test; public class EnviromentVariables { @Test public void test(){ // System.getenv() for(Map.Entry entry : System.getenv().entrySet()){ System.out.println(entry.getKey()+" : "+entry.getValue()); } } }
- 不存在任何數組到Iterable的自動轉換,必須手工轉換
- Arrays.asList()產生的List對象會使用底層數組作爲其物理實現,所以修改這個返回的List會修改底層的數組,如果不希望修改,則需要創建一個副本,如用new ArrayList<>包裝一下創建一個引用副本