原始碼閱讀之Java棧的實現
0x00 棧
棧是 Last-In-First-Out
(後進先出)的線性表。對棧的操作主要有兩個:入棧(push)和出棧(pop)。因此它也是一種操作受限的線性表。儘管如此,它在計算機中應用非常廣泛,是一種非常基礎的資料結構。
0x01 原始碼
從原始碼中可以看出棧也是一種非常簡單的資料結構。棧的原始碼非常簡潔,只有100多行程式碼。
public class Stack<E> extends Vector<E> {
public Stack() {
}
//入棧操作
public E push(E item) {
addElement(item);
return item;
}
//出棧操作
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
//檢視棧元素,不會刪除
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
//檢查是否為空棧
public boolean empty() {
return size() == 0;
}
//查詢元素,如果找到則返回從棧頂開始計數的位置,否則返回-1
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = 1224463164541339165L;
}
它繼承於 Vector
類,這個跟前面講的 ArrayList
是一樣的,只不過 Vector
類方法是同步的,因此 Stack
也是執行緒安全的。
push
入棧
public E push(E item) {
addElement(item);
return item;
}
雖然此方法沒有宣告 synchronized
,但內部呼叫一個 addElement
來實現入棧操作。這個操作方法是在父類中實現的,它被宣告為執行緒安全的,因此它也是執行緒安全的。
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//擴容方法
//這裡跟 ArrayList 擴容演算法有點不一樣,ArrayList 一次是擴容為原來的1.5倍,Vector 預設擴容為原來的1倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
雖然 push
方法沒有宣告 synchronized
但 addElement
方法中有。在這個方法裡
- 首先把
modCount
加 1,記錄一次對資料結構的操作。 - 然後檢查陣列容量是否足夠,不夠則擴容。
- 最後把元素物件新增到陣列末尾。
需要注意的是Vector
的擴容策略是預設一次擴容為原來的1倍,這與ArrayList
一次擴容原來的1.5倍不同。
pop
出棧
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
pop
方法被宣告為 synchronized
,是執行緒安全的方法。它通過 peek
方法獲取到棧頂元素物件,然後呼叫父類的 removeElementAt
方法把棧頂元素刪除。
peek
檢視棧頂元素
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
這個方法也是執行緒安全的。可以看出如果棧的大小為0時,執行 peek
方法會丟擲 EmptyStackException
異常。
search
在棧中搜尋元素
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
繼續檢視父類程式碼
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
if (o == null) {//如果查詢的元素是空的則遍歷找到最接近棧的空元素
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))//通過equals方法來判斷兩個元素是否相同
return i;
}
return -1;
}
在棧中查詢一個元素需要遍歷,時間複雜度為O(n)。
0x02 總結
棧是一種LIFO
的資料結構,它基於 Vector
來實現,所有的方法都是執行緒安全的。入棧和出棧操作的時間複雜度為O(1),查詢元素的時間複雜度為O(n)。
相關文章
- 原始碼閱讀之LinkedList實現細節原始碼
- 原始碼閱讀之ArrayList實現細節原始碼
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- [Redis原始碼閱讀]dict字典的實現Redis原始碼
- 如何閱讀Java原始碼?Java原始碼
- Redis原始碼閱讀:sds字串實現Redis原始碼字串
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- java 8 HashMap 原始碼閱讀JavaHashMap原始碼
- Java原始碼跟蹤閱讀技巧Java原始碼
- Java類載入原始碼閱讀Java原始碼
- Java原始碼閱讀之TreeMap(紅黑樹) - JDK1.8Java原始碼JDK
- [Redis原始碼閱讀]實現一個redis命令--nonzerodecrRedis原始碼
- java8LinkedList原始碼閱讀解析Java原始碼
- TiDB 原始碼閱讀系列文章(五)TiDB SQL Parser 的實現TiDB原始碼SQL
- Spring 原始碼閱讀之標籤解析Spring原始碼
- 【初學】Spring原始碼筆記之零:閱讀原始碼Spring原始碼筆記
- 閱讀vue原始碼後,簡單實現虛擬domVue原始碼
- Vue原始碼閱讀一:說說vue.nextTick實現Vue原始碼
- Java原始碼閱讀-String中的private final char value[];Java原始碼
- AmplifyImpostors原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- AQS原始碼閱讀AQS原始碼
- delta原始碼閱讀原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- ConcurrentHashMap原始碼閱讀HashMap原始碼
- HashMap原始碼閱讀HashMap原始碼
- Mux 原始碼閱讀UX原始碼
- ReactorKit原始碼閱讀React原始碼
- Vollery原始碼閱讀(—)原始碼
- NGINX原始碼閱讀Nginx原始碼
- ThreadLocal原始碼閱讀thread原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- Runtime 原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼