java代码中Stack实现原理

栈是java存放内存的两种结构之一。栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线形表。

这种数据结构,它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。 也就是说,栈是只能在某一端插入和删除的特殊线性表。

我们可以将栈理解成一个杯子,或者是一个桶,而桶的底部则是实心的。这样我们存放东西的东西,就会从下到上一件件的存放,而取出东西时,只能从最上面的那个先拿。

那么这种数据结构有什么好处呢。我们举一个例子,当A方法调用B方法,B方法再调用C方法,那么他的进栈顺序就是A→B→C,而出栈的方法,也只能是C→B→A,因为A中的后续方法可能会用到B或C中的结果,所以我们必须先执行完C才能调用B的后续方法,继而调用A的后续方法。

而在java中,也在util中为我们模拟了stack这种数据结构。

public class Stack<E> extends Vector<E>

首先看类的定义,这里stack方法继承了Vector类,而Vertor是什么对象呢,打开对象看一下。

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

也就是说Vertor继承了抽象的AbstractList类,同时继承了List借口,这下可以妥妥的确定,Vertor是一个List类型的数据了。也就是说是一个集合。而为什么现在Vector现在很难看到了呢,因为Vector是线程安全的,其余功能与ArrayList完全一致,这也就导致了为什么Vector使用的减少。也就是说,stack底层的结构其实是一个Collection集合。

继续往下看,就是Stack的构造方法,Stack由于其特征,我们在初始化的时候,是不好给它传入数据的。
栈的方法列表

/**
     * Creates an empty Stack.
     */
    public Stack() {
    }

提供空参构造方法,里面隐式的调用了父类的构造方法

/**
     * Constructs an empty vector so that its internal data array
     * has size {@code 10} and its standard capacity increment is
     * zero.
     */
    public Vector() {
        this(10);
    }

接着父类调用他的有参构造方法:

/**
    * Constructs an empty vector with the specified initial capacity and
    * with its capacity increment equal to zero.
    *
    * @param   initialCapacity   the capacity of the vector
    * @throws IllegalArgumentException if the specified initial capacity
    *         is negative
    */
   public Vector(int initialCapacity) {
       this(initialCapacity, 0);
   }

可以看到这边继续往上调用了两个int参数的构造方法,这是intialCapacity为10,那么第二个int参数是什么意义呢?

public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

这边super方法不用继续看,因为AbstractList中的构造方法也是空构造函数。initalCapacity就是初始化时,初始化的数组的容量,而capacityIncrement则是在数组容器不够使用时,用于控制每次增加数组容量的个数。

    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);
    }

这边则是用来控制数组增长的方法,也就是说在stack中也可以自动增长,当然我们栈中每次增加的数量,就不受我们控制了。

也就是说,当我们调用一个new Stack()时,就会给我们生成一个内部容量为10的数组。

对于栈而言,当生成完毕之后,需要哪些方法呢,也就是,放,取,以及查看这些动作。当然其他的方法也能实现,不过对于栈而言,这些功能也不是很必要。

首先第一个方法,push方法:

public E push(E item) {
        addElement(item);

        return item;
    }

直接调用addElement方法:

public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

将计数器加1,然后确认一下数组容量是否足够,之后在相应位置也就是栈顶插入元素。

弹栈方法:

public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1);

        return obj;
    }

这边的peek方法便是查看栈顶元素,后调用removeElementAt,将len-1位置的元素,也就是最后一个(栈顶)的元素删除,达到出栈的目的。

查看栈顶元素:

public synchronized E peek() {
        int     len = size();

        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }

直接返回len-1位置的元素,便也是栈顶元素。

此外还提供了empty方法来判断该栈是不是空的元素,以及search方法,查找对应元素的角标。

猜你喜欢

转载自blog.csdn.net/m0_37869177/article/details/88847569