最近找工作时,面试了阿里、百度、腾讯、字节等一线互联网大厂。
发现这些大厂有一个共同特征,就是喜欢考查面试者查看底层代码的能力。
于是就有了这个系列, 希望能够帮到大家。
先来看一下Stack的全部源码
package java.util; public class Stack<E> extends Vector<E> { private static final long serialVersionUID = 1224463164541339165L; public Stack() { } public E push(E var1) { this.addElement(var1); return var1; } public synchronized E pop() { int var2 = this.size(); Object var1 = this.peek(); this.removeElementAt(var2 - 1); return var1; } public synchronized E peek() { int var1 = this.size(); if (var1 == 0) { throw new EmptyStackException(); } else { return this.elementAt(var1 - 1); } } public boolean empty() { return this.size() == 0; } public synchronized int search(Object var1) { int var2 = this.lastIndexOf(var1); return var2 >= 0 ? this.size() - var2 : -1; } }
我们发现,Stack是继承Vector实现栈原理的。
接下来一个一个方法来进行分析:
声明语句:Stack<Integer>st = new Stack<Integer>();
注意:Stack中只能压入包装类,即只能声明Integer,没办法声明int。
源码:
// push源码 public E push(E var1) { this.addElement(var1); return var1; }
这里我们发现它代用了一个addElement方法。这个方法基于synchronized修饰,是一个线程安全的方法。
点进去查看, 发现跳转到了vector源代码中,证明Stack与Vector共用一个添加元素的方法(addElement)。
该方法首先要判断数组的容量是否能够容纳新的元素,若不能,则需要进行扩容操作,然后将元素e放置在数组的size位置。ensureCapacityHelper(int)方法源码如下
// addElement 方法 public synchronized void addElement(E obj) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; }
在ensureCapacityHelper()方法中,判断数组实际容纳的元素数量是否超过了当前数组长度,若是,则执行扩容操作, 即grow方法。
// ensureCapacityHelper 方法 private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code // 如果超过了数组长度,则需要扩容。 if (minCapacity - elementData.length > 0) grow(minCapacity); }
// grow 方法 private void grow(int minCapacity) { // overflow-conscious code // 获取elementData的原始容量 int oldCapacity = elementData.length; // 计算新的容量 // 如果在构造方法中设置了capacityIncrement > 0,那么新数组长度就是原数组长度 + capacityIncrement // 否则,新数组长度就是原数组长度 * 2 int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); // 若进行扩容后,capacity仍然比实际需要的小,则新容量更改为实际需要的大小,即minCapacity if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果新数组的长度比虚拟机能够提供给数组的最大存储空间大,则将新数组长度更改为最大正数:Integer.MAX_VALUE if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 按照新的容量newCapacity创建一个新数组,然后再将原数组中的内容copy到新数组中 elementData = Arrays.copyOf(elementData, newCapacity); }
注意:扩容后的数组长度为原数组长度*2
以上,即为Stack中push的全部源码分析。
Pop的源码:
该方法被synchronized修饰,是线程安全的。
var2为当前栈长度,var1为栈顶元素,首先通过removeElementAt()方法移除栈顶元素,然后返回被移除的栈顶元素值。
public synchronized E pop() { int var2 = this.size(); Object var1 = this.peek(); this.removeElementAt(var2 - 1); return var1; }
接下来查看removeElementAt()方法
public synchronized void removeElementAt(int index) { // index是移除一个元素后数组的长度 // index大于等于元素个数,则抛出异常 modCount++; if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } // index小于0,则抛异常 // ArrayIndexOutOfBoundsException:数组索引越界异常 else if (index < 0) { throw new ArrayIndexOutOfBoundsException(index); } // 如果一切正常,执行弹出操作。 int j = elementCount - index - 1; if (j > 0) { System.arraycopy(elementData, index + 1, elementData, index, j); } elementCount--; elementData[elementCount] = null; /* to let gc do its work */ }
如果为0,则返回空栈异常
反之,进行元素查找并返回的操作(elementAt()方法)
public synchronized E peek() { int var1 = this.size(); if (var1 == 0) { throw new EmptyStackException(); } else { return this.elementAt(var1 - 1); } }
public boolean empty() { return this.size() == 0; }
lastIndexOf()方法:从后向前查找元素,若查找成功,则返回位置,反之,返回-1。
var2存储查找结果,如果var2>=0,则证明查找成功,则返回位置
public synchronized int search(Object var1) { int var2 = this.lastIndexOf(var1); return var2 >= 0 ? this.size() - var2 : -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])) return i; } return -1; }