接下來咱們進入集合學習,看過不少文章一上來就是講解原理感受會特別枯燥,任何成熟解決方案的出現都是爲了解決問題,若經過實際問題引入而後再來說解原理想必學起來一定事半功倍,從我寫博客的那一天起,我就在思考如何經過通俗易懂的話讓看到文章的童鞋立馬能明白我講解的什麼,即便文章很長如果層層遞進定不會感到枯燥乏味,因此我腦海裏一直在高度不停旋轉着去找合適的例子。關於集合學習將分爲例子引入、源碼分析、數據結構分析三個部分來進行闡述。數組
咱們以一個例子來進行集合入門講解,上學時咱們上體育課,首先體育老師會叫咱們多少多少同窗站成一對來進行報名,而後自由解散休息,哈哈。體育老師要求咱們站成一對,這個時候就好對數組進行數據存儲,咱們假設體育老師須要5我的站成一對,因此這也就對應數組初始化的容量,有了容量以後,接下來則是在所要求的地方站到指定位置,這就比如往數組裏添加元素,好了接下來咱們以Java語言來實現這一要求。首先咱們定義一個排隊類,依據面向對象封裝思想,咱們對外提供操做方法,因此在此類中定義私有的數組,而後定義集合中元素個數屬性,以下:數據結構
public class QueueDemo { private int Size; private Integer[] Elements; }
依據咱們所分析,咱們按照5人站成一對,因此當咱們初始化排隊類時就初始化數組容量,也就是說咱們在構造函數中初始化容量,以下:app
public class QueueDemo { // 數組大小 private int Size; // 數組 private Integer[] Elements; // 初始化數組容量 public QueueDemo(int capacity) { Elements = new Integer[capacity]; } }
咱們初始化容量後呢,接下來則是對應同窗開始排隊,此時也就是對應往數組裏添加元素,因此咱們封裝一個添加方法,每添加一個元素則數組大小則遞增1,以下:ide
// 添加元素 public void Add(Integer element) { Elements[Size] = element; Size++; }
對應每一步操做,咱們都遍歷打印出數組中元素,因此接下來咱們重寫toString方法,以下:函數
// 重寫toString打印元素 @Override public String toString() { int length = Elements.length; StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < length; i++) { sb.append(Elements[i]); if (i != length - 1) { sb.append(","); } } return sb.toString(); }
咱們完成了同窗排隊第一步,接下來咱們實例化上述排隊類並添加元素(咱們將元素看作時排隊時同窗們的姓名),最後打印元素,以下:源碼分析
public class Main { public static void main(String[] args) { QueueDemo demo = new QueueDemo(5); demo.Add(1); demo.Add(2); demo.Add(3); demo.Add(4); demo.Add(5); System.out.println(demo); } }
當同窗們排成一隊後,體育老師發現排隊的同窗高矮不一,而後將同窗與同窗之間按照高矮進行調換,這也就對應着咱們須要封裝插入元素的方法,由於咱們初始化數組容量爲5,當咱們在指定索引插入一個元素時,再打印元素必然拋出數組異常,也就是涉及到數組容量擴容,咱們暫且定義在指定索引插入元素的方法,以下:性能
public void Insert(int index, Integer element) { }
當按照高矮排好隊後,體育老師認爲一列只需排4我的,剩餘一我的到其餘對去,這也就對應刪除數組中的元素,一樣咱們定義刪除方法,當咱們刪除數組中元素時,須要將刪除的元素後的元素都往前移一位,同時將最後一位置爲空,數組大小也減小一位,以下:學習
// 刪除元素 public void Remove(Integer element) { int index = GetIndex(element); for (int i = index; i < Elements.length - 1; i++) { Elements[i] = Elements[i + 1]; } Elements[Size - 1] = null; Size--; }
接下來咱們再來刪除並打印元素,以下:ui
//刪除元素 demo.Remove(3); System.out.println(demo);
由於咱們將數組最後一位元素置爲空,因此在打印時應刪除,咱們繼續改造重寫的toString方法,以下:this
@Override public String toString() { int length = Elements.length; StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < length; i++) { if (Elements[i] == null) { continue; } sb.append(Elements[i]); if (i != length - 1) { sb.append(","); } } if (sb.charAt(sb.length() - 1) == ',') { sb.delete(sb.length() - 1, sb.length()); } sb.append("]"); return sb.toString(); }
接下來體育老師要求報數,好比根據某個同窗的姓名即元素報出本身所在的第幾位(也就對應數組中的索引),因此此時咱們再封裝一個獲取指定元素的索引方法,以下:
// 獲取指定元素索引 public int GetIndex(Integer element) { for (int i = 0; i < Elements.length - 1; i++) { if (!Elements[i].equals(element)) { continue; } return i; } throw new RuntimeException("未找到"); }
而後咱們嘗試獲取4號同窗所排隊的位置是,以下:
System.out.println("4號同窗所在的位置是 :" + demo.GetIndex(4));
到了這裏咱們完成了排隊的基本要求,可是還遠遠不夠,好比咱們是自定義初始化容量,這裏咱們指定爲5,通過刪除操做後,最終數組中存在4個元素,要是咱們再往數組中添加至少2個以上元素,此時打印數組元素將拋出異常,因此這裏爲了解決這個問題咱們對數組進行自動擴容,也就是對添加方法進行改造,當添加元素時咱們須要判斷是否已經超過數組容量,若超過,咱們將數組容量擴大到現有數組容量的2倍,那麼咱們應該怎麼判斷呢?咱們經過數組大小和數組容量進行判斷,以下:
public void Add(Integer element) { if (Size >= Elements.length) { Elements = Arrays.copyOf(Elements, 2 * Elements.length); } Elements[Size] = element; Size++; }
public static void main(String[] args) { QueueDemo demo = new QueueDemo(5); demo.Add(1); demo.Add(2); demo.Add(3); demo.Add(4); demo.Add(5); System.out.println(demo); //刪除元素 demo.Remove(3); System.out.println(demo); System.out.println("4號同窗所在的位置是 :" + demo.GetIndex(4)); demo.Add(6); demo.Add(7); System.out.println(demo);
}
在排隊時咱們給定人數爲5,也就說數組初始化容量爲5,這還不夠靈活,若是體育老師已經明確規定一列必須站幾個,咱們直接就能接受到體育老師規定的信號,這就像明確指定了數組的初始化容量,這樣一來既能保證不會拋出異常,同時也不會影響當添加和插入元素時擴容時所帶來的性能開銷,若是未明確規定一列站幾個,咱們也能夠默認初始化容量,如此最靈活,一切都未變且性能最佳,以下咱們定義一個默認初始化容量並改造排隊列構造函數,以下:
private int DEFAULT_CAPACITY = 10; public QueueDemo() { Elements = new Integer[DEFAULT_CAPACITY]; } public QueueDemo(int capacity) { Elements = new Integer[capacity <= 0 ? DEFAULT_CAPACITY : capacity]; }
到了這裏咱們還未實如今指定索引位置插入元素的Insert方法,既然要插入指定索引位置,首先咱們必須檢查指定索引位置是否超過數組大小,而後將指定索引後的元素向後移動一位,最後留出指定索引位置進行插入,以下:
public void Insert(int index, Integer element) { if (Size <= index || index < 0) { throw new RuntimeException("超出數組邊界"); } System.arraycopy(Elements, index, Elements, index + 1, Size - index); System.out.println(this); Elements[index] = element; Size++; }
QueueDemo demo = new QueueDemo(); demo.Add(1); demo.Add(2); demo.Add(3); demo.Add(4); demo.Add(5); System.out.println(demo); //刪除元素 demo.Remove(3); System.out.println(demo); System.out.println("4號同窗所在的位置是 :" + demo.GetIndex(4)); demo.Add(6); demo.Add(7); System.out.println(demo); //插入元素 demo.Insert(2, 20); System.out.println(demo);
如上咱們首先檢查指定索引是否小於0或者是否超出數組大小,不然拋出異常,而後這裏咱們經過內置提供的方法,從指定索引位置後的元素進行復制即Index+1,最後複製元素的長度爲Size-Index。此時指定索引位置數據仍爲4,最後咱們將指定索引位置的值經過咱們要插入的值進行替換。
如上則是咱們實現比較完整的排隊需求,固然還有一些參數檢查的小問題,看到這裏想必不少童鞋就已經清楚知道了,其實咱們實現的就是Java中的集合,有了本節課的基礎,下節課咱們進行ArrayList源碼分析將駕輕就熟,感謝閱讀,咱們下節見。