給jdk寫註釋系列之jdk1.6容器(10)-Stack&Vector源碼解析

来源:http://www.cnblogs.com/tstd/archive/2016/01/05/5104099.html
-Advertisement-
Play Games

前面我們已經接觸過幾種數據結構了,有數組、鏈表、Hash表、紅黑樹(二叉查詢樹),今天再來看另外一種數據結構:棧。 什麼是棧呢,我就不找它具體的定義了,直接舉個例子,棧就相當於一個很窄的木桶,我們往木桶里放東西,往外拿東西時會發現,我們最開始放的東西在最底部,最先拿出來的是剛剛放進去的。所以,...


  前面我們已經接觸過幾種數據結構了,有數組、鏈表、Hash表、紅黑樹(二叉查詢樹),今天再來看另外一種數據結構:棧。      什麼是棧呢,我就不找它具體的定義了,直接舉個例子,棧就相當於一個很窄的木桶,我們往木桶里放東西,往外拿東西時會發現,我們最開始放的東西在最底部,最先拿出來的是剛剛放進去的。所以,棧就是這麼一種先進後出First In Last Out,或者叫後進先出 的容器,它只有一個口,在這個口放入元素,也在這個口取出元素     棧最主要了兩個動作就是入棧和出棧操作,其實還是很容易的明白的對不對,那麼我們接下來就看一下Jdk容器中的棧Stack是怎麼實現的吧。    1.定義
1 public
2 class Stack<E> extends Vector<E> {

  我們發現Stack繼承了Vector,Vector又是什麼東東呢,看一下。

1 public class Vector<E>
2     extends AbstractList<E>
3     implements List<E>, RandomAccess, Cloneable, java.io.Serializable

  發現沒有,Vector是List的一個實現類,其實Vector也是一個基於數組實現的List容器,其功能及實現代碼和ArrayList基本上是一樣的。那麼不一樣的是什麼地方的,一個是數組擴容的時候,Vector是*2,ArrayList是*1.5+1;另一個就是Vector是線程安全的,而ArrayList不是,而Vector線程安全的做法是在每個方法上面加了一個synchronized關鍵字來保證的。但是這裡說一句,Vector已經不官方的(大家公認的)不被推薦使用了,正式因為其實現線程安全方式是鎖定整個方法,導致的是效率不高,那麼有沒有更好的提到方案呢,其實也不能說有,但是還真就有那麼一個,Collections.synchronizedList(),這不是我們今天的重點不做深入探討,回到Stack的實現上。

  

2.Stack&Vector底層存儲        由於Stack是繼承和基於Vector,那麼簡單看一下Vector的一些定義和方法源碼:
 1     // 底層使用數組存儲數據
 2     protected Object[] elementData;
 3     // 元素個數
 4     protected int elementCount ;
 5     // 自定義容器擴容遞增大小
 6     protected int capacityIncrement ;
 7 
 8     public Vector( int initialCapacity, int capacityIncrement) {
 9         super();
10         // 越界檢查
11         if (initialCapacity < 0)
12             throw new IllegalArgumentException( "Illegal Capacity: " +
13                                                initialCapacity);
14         // 初始化數組
15         this.elementData = new Object[initialCapacity];
16         this.capacityIncrement = capacityIncrement;
17     }
18  
19     // 使用synchronized關鍵字鎖定方法,保證同一時間內只有一個線程可以操縱該方法
20     public synchronized boolean add(E e) {
21         modCount++;
22        // 擴容檢查
23        ensureCapacityHelper( elementCount + 1);
24         elementData[elementCount ++] = e;
25         return true;
26     }
27  
28     private void ensureCapacityHelper(int minCapacity) {
29         // 當前元素數量
30         int oldCapacity = elementData .length;
31         // 是否需要擴容
32         if (minCapacity > oldCapacity) {
33            Object[] oldData = elementData;
34            // 如果自定義了容器擴容遞增大小,則按照capacityIncrement進行擴容,否則按兩倍進行擴容(*2)
35            int newCapacity = (capacityIncrement > 0) ?
36               (oldCapacity + capacityIncrement) : (oldCapacity * 2);
37            if (newCapacity < minCapacity) {
38               newCapacity = minCapacity;
39            }
40            // 數組copy
41             elementData = Arrays.copyOf( elementData, newCapacity);
42        }
43     }
  Vector就簡單看到這裡,其他方法Stack如果沒有調用的話就不進行分析了,不明白的可以去看ArrayList源碼解析。   3.peek()——獲取棧頂的對象
 1     /**
 2      * 獲取棧頂的對象,但是不刪除
 3      */
 4     public synchronized E peek() {
 5         // 當前容器元素個數
 6         int   len = size();
 7      
 8         // 如果沒有元素,則直接拋出異常
 9         if (len == 0)
10            throw new EmptyStackException();
11         // 調用elementAt方法取出數組最後一個元素(最後一個元素在棧頂)
12         return elementAt(len - 1);
13     }
14  
15     /**
16      * 根據index索引取出該位置的元素,這個方法在Vector中
17      */
18     public synchronized E elementAt(int index) {
19         // 越界檢查
20         if (index >= elementCount ) {
21            throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
22        }
23 
24         // 直接通過數組下標獲取元素
25         return (E)elementData [index];
26     }

 

4.pop()——彈棧(出棧),獲取棧頂的對象,並將該對象從容器中刪除

 1     /**
 2      * 彈棧,獲取並刪除棧頂的對象
 3      */
 4     public synchronized E pop() {
 5         // 記錄棧頂的對象
 6        E      obj;
 7         // 當前容器元素個數
 8         int   len = size();
 9 
10        // 通過peek()方法獲取棧頂對象
11        obj = peek();
12        // 調用removeElement方法刪除棧頂對象
13        removeElementAt(len - 1);
14 
15        // 返回棧頂對象
16         return obj;
17     }
18 
19     /**
20      * 根據index索引刪除元素
21      */
22     public synchronized void removeElementAt(int index) {
23         modCount++;
24         // 越界檢查
25         if (index >= elementCount ) {
26            throw new ArrayIndexOutOfBoundsException(index + " >= " +
27                                               elementCount);
28        }
29         else if (index < 0) {
30            throw new ArrayIndexOutOfBoundsException(index);
31        }
32         // 計算數組元素要移動的個數
33         int j = elementCount - index - 1;
34         if (j > 0) {
35            // 進行數組移動,中間刪除了一個,所以將後面的元素往前移動(這裡直接移動將index位置元素覆蓋掉,就相當於刪除了)
36            System. arraycopy(elementData, index + 1, elementData, index, j);
37        }
38         // 容器元素個數減1
39         elementCount--;
40         // 將容器最後一個元素置空(因為刪除了一個元素,然後index後面的元素都向前移動了,所以最後一個就沒用了 )
41         elementData[elementCount ] = null; /* to let gc do its work */
42     }

 

5.push(E item)——壓棧(入棧),將對象添加進容器並返回

 1     /**
 2      * 將對象添加進容器並返回
 3      */
 4     public E push(E item) {
 5        // 調用addElement將元素添加進容器
 6        addElement(item);
 7        // 返回該元素
 8         return item;
 9     }
10 
11     /**
12      * 將元素添加進容器,這個方法在Vector中
13      */
14     public synchronized void addElement(E obj) {
15         modCount++;
16        // 擴容檢查
17        ensureCapacityHelper( elementCount + 1);
18        // 將對象放入到數組中,元素個數+1
19         elementData[elementCount ++] = obj;
20     }

 

6.search(Object o)——返回對象在容器中的位置,棧頂為1

 1     /**
 2      * 返回對象在容器中的位置,棧頂為1
 3      */
 4     public synchronized int search(Object o) {
 5         // 從數組中查找元素,從最後一次出現
 6         int i = lastIndexOf(o);
 7 
 8         // 因為棧頂算1,所以要用size()-i計算
 9         if (i >= 0) {
10            return size() - i;
11        }
12         return -1;
13     }

 

7.empty()——容器是否為空

1     /**
2      * 檢查容器是否為空
3      */
4     public boolean empty() {
5         return size() == 0;
6     }
  到這裡Stack的方法就分析完成了,由於Stack最終還是基於數組的,理解起來還是很容易的(因為有了ArrayList的基礎啦)。            雖然jdk中Stack的源碼分析完了,但是這裡有必要討論下,不知道是否發現這裡的Stack很奇怪的現象,      (1)Stack為什麼是基於數組實現的呢?           我們都知道數組的特點:方便根據下標查詢(隨機訪問),但是記憶體固定,且擴容效率較低。很容易想到Stack用鏈表實現最合適的。      (2)Stack為什麼是繼承Vector的?           繼承也就意味著Stack繼承了Vector的方法,這使得Stack有點不倫不類的感覺,既是List又是Stack。如果非要繼承Vector合理的做法應該是什麼:Stack不繼承Vector,而只是在自身有一個Vector的引用,聚合對不對?        唯一的解釋呢,就是Stack是jdk1.0出來的,那個時候jdk中的容器還沒有ArrayList、LinkedList等只有Vector,既然已經有了Vector且能實現Stack的功能,那麼就乾吧。。。        既然用鏈表實現Stack是比較理想的,那麼我們就來嘗試一下吧:
 1 import java.util.LinkedList;
 2 
 3 public class LinkedStack<E> {
 4 
 5         private LinkedList<E> linked ;
 6 
 7         public LinkedStack() {
 8                this.linked = new LinkedList<E>();
 9        }
10 
11         public E push(E item) {
12                this.linked .addFirst(item);
13                return item;
14        }
15 
16         public E pop() {
17                if (this.linked.isEmpty()) {
18                       return null;
19               }
20                return this.linked.removeFirst();
21        }
22 
23         public E peek() {
24                if (this.linked.isEmpty()) {
25                       return null;
26               }
27                return this.linked.getFirst();
28        }
29        
30         public int search(E item) {
31                int i = this.linked.indexOf(item);
32                return i + 1;
33        }
34        
35         public boolean empty() {
36                return this.linked.isEmpty();
37        }
38 }
  看完後,你說我cha,為什麼這麼簡單,就這麼點代碼。因為這裡使用的LinkedList實現的Stack,記得在LinkedList中說過,LinkedList實現了Deque介面使得它既可以作為棧(先進後出),又可以作為隊列(先進先出)。那麼什麼是隊列呢?Queue見吧!            Stack&Vector 完!      參見: 給jdk寫註釋系列之jdk1.6容器(1)-ArrayList源碼解析 給jdk寫註釋系列之jdk1.6容器(2)-LinkedList源碼解析

 

 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有的小伙伴會問:博主,沒有Mac怎麼學Swift語言呢,我想學Swift,但前提得買個Mac。非也,非也。如果你想瞭解或者初步學習Swift語言的話,你可以登錄這個網站:http://swiftstub.com/。該網站可以線上運行出代碼結果,也可以說這是一個線上的Playground。你可以...
  • Path通常代表文件系統中的位置,能瀏覽任何類型的文件系統,包括zip歸檔文件系統;文件系統中的幾個概念:目錄樹、根目錄、絕對路徑、相對路徑;NIO.2中的Path是一個抽象構造,你所創建和處理的Path可以不馬上綁定到對應的物理位置上;——物理文件系統的處理通常由Files輔助類實現;基礎類類說明...
  • 當在網上問為什麼Python比C語言更慢,回答最多的就是Python中有動態類型。然而,動態類型確實會在性能方面有影響,但是這並不是主要原因。 動態類型(像Python一樣的主要編程語言都一樣)使得編譯器很難優化性能。動態使得每次執行都可能很不同,編譯器難以優化。然而,正如Alex在談話中提到的,....
  • C++中的const可用於修飾變數、函數,且在不同的地方有著不同的含義,現總結如下。const的語義C++中的const的目的是通過編譯器來保證對象的常量性,強制編譯器將所有可能違背const對象的常量性的操作都視為error。對象的常量性可以分為兩種:物理常量性(即每個bit都不可改變)和邏輯常量...
  • 一,java.util.regex包中提供了兩個類來表示對正則表達式的支持1.Matcher,通過解釋Pattern對character sequence 執行匹配操作的引擎public final class Matcher implements MatchResult2.Pattern,正則表達...
  • 1.1 框架的概念框架其實就是可重用代碼的集合,框架的代碼是框架架構的代碼,不是業務邏輯代碼,框架代碼保護類.方法.函數等等,框架代碼按照一定的規則組合起來就形成了框架。1.2 不使用框架開發的時候遇到的問題 1.代碼編寫沒有統一的規範 2.項目功能不能很好的拆分 3.一個局部的微小改動可能會...
  • 五種I/O: 1)阻塞I/0 2)非阻塞I/O 3)I/O復用 4)事件(信號)驅動I/O 5)非同步I/O
  • 接著上一篇博客繼續Tomcat配置。3. 虛擬目錄映射虛擬目錄是與實際目錄相對應的,不是一個實際存在的目錄。配置虛擬目錄有兩點好處:1、 便於理解;2、如果web應用所在目錄更改,只需要更改虛擬目錄對應的實際目錄,而外界仍可以通過原方式訪問新的web應用。Tomcat 中配置虛擬目錄有以下三中方式:...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...