栈作为常用的数据存储结构之一,存储的特点是“先进后出”,通常都是直接拿Java提供的Stack使用,那我们也可以自己动手去实现下栈,栈的存储结构分为顺序存储和链式存储,本篇先实现顺序存储。
一、基本实现
栈的顺序存储主要靠数组来存储数据,栈中还有一个比较重要的就是“栈顶指针”,不管入栈、出栈还是查看栈顶元素都要用到“栈顶指针”,所以这里可以把“栈顶指针”定义成一个int变量,等于数组最后一个元素的下标。
下面是具体的代码:
public class OrderStack {
// 保存数据的数组
private Object[] data;
// 栈顶指针
private int top = -1;
// 栈中元素个数
private int size;
/**
* 无参构造,默认创建大小为10的数组
*/
public OrderStack() {
this(10);
}
/**
* 带参构造,指定创建数组的大小
*
* @param initialSize 数组的大小
*
*/
public OrderStack(int initialSize) {
data = new Object[initialSize];
}
/**
* 入栈
* @param obj 入栈的数据
*
*/
public void push(Object obj) {
if (top >= data.length - 1) {
// 元素个数超出数组大小
System.out.println("栈满");
} else {
data[++top] = obj;
size++;
}
}
/**
* 出栈
* @return 返回栈顶的元素栈顶元素
*/
public Object pop() {
if (isEmpty()) {
// 栈为空时抛出异常
throw new RuntimeException("栈为空");
} else {
Object obj = data[top--];
size--;
data[size] = null; // help GC
return obj;
}
}
/**
* 查看栈顶元素,但并不出栈
* @return 返回栈顶的元素
*/
public Object peek() {
if (isEmpty()) {
// 栈为空时抛出异常
throw new RuntimeException("栈为空");
} else {
return data[top];
}
}
/**
* 判断栈是否为空
* @return 为空返回true,否则false
*/
public boolean isEmpty() {
return top == -1 ? true : false;
}
/**
* 返回栈中元素的个数
* @return 栈中元素的个数
*/
public int size() {
return this.size;
}
}
测试如下:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
OrderStack orderStack = new OrderStack();
orderStack.push(10);
orderStack.push(20);
orderStack.push(30);
orderStack.push(40);
orderStack.push(50);
orderStack.push(60);
orderStack.push(70);
orderStack.push(80);
orderStack.push(90);
orderStack.push(100);
System.out.println("栈中元素个数:" + orderStack.size());
System.out.println("栈顶元素:" +orderStack.peek());
System.out.println("=============楚河汉界=============");
System.out.println("出栈:" +orderStack.pop());
System.out.println("栈中元素个数:" + orderStack.size());
System.out.println("=============楚河汉界=============");
}
}
运行结果如下,出栈、查看栈顶元素、入栈都可以实现了:
二、push()方法的改进
但是如果我紧接着在入栈5个数据就会报“栈满”的提示,因为正在入栈方法中做了相应的处理,避免入栈的数据超出数组的大小。
public static void main(String[] args) {
// ...上面代码省略...
System.out.println("出栈:" +orderStack.pop());
System.out.println("栈中元素个数:" + orderStack.size());
System.out.println("=============楚河汉界=============");
// 再入栈5个
orderStack.push(200);
orderStack.push(210);
orderStack.push(220);
orderStack.push(230);
orderStack.push(240);
}
这个问题需要解决掉,毕竟这样的栈限制太大,可以在push方法中利用Arrays类中的copyOf()方法在数组满时动态的创建长度更长的数组,并将原数组的数据拷贝进去。
新的push()方法:
/**
* 入栈
* @param obj 入栈的数据
*/
public void push(Object obj) {
if (top >= data.length - 1) {
// 当数组满了,就创建一个新数组,长度为当前数组的2倍,并将当前数组的数据拷贝进去
data = Arrays.copyOf(data, data.length * 2);
}
data[++top] = obj;
size++;
}
这样不管插入多少个数据都没有问题了:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
OrderStack orderStack = new OrderStack();
orderStack.push(10);
orderStack.push(20);
orderStack.push(30);
orderStack.push(40);
orderStack.push(50);
orderStack.push(60);
orderStack.push(70);
orderStack.push(80);
orderStack.push(90);
orderStack.push(100);
orderStack.push(200);
orderStack.push(210);
orderStack.push(220);
orderStack.push(230);
orderStack.push(240);
System.out.println("栈中元素个数:" + orderStack.size());
System.out.println("=============楚河汉界=============");
}
}
三、进一步完善
上面在定义时数组类型为Object,在push方法中定义的参数也为Object,这样就带来一个问题,我入栈的时候不管是什么类型的数据都可入栈,这样我们的栈就变成了“杂货铺”不便于使用,现在就需要通过使用泛型来约束栈保存数据的类型。代码主体不需要更改,只需要做一些更改。
public class **OrderStack<T>** {
// 保存数据的数组
private Object[] data;
// 栈顶指针
private int top = -1;
// 栈中元素个数
private int size;
/**
* 无参构造,默认创建大小为10的数组
*/
public OrderStack() {
this(10);
}
/**
* 带参构造,指定创建数组的大小
*
* @param initialSize
* 数组的大小
*
*/
public OrderStack(int initialSize) {
data = new Object[initialSize];
}
/**
* 入栈
* @param obj 入栈的数据
*/
public void push(**T obj**) {
if (top >= data.length - 1) {
// 当数组满了,就创建一个新数组,长度为当前数组的2倍,并将当前数组的数据拷贝进去
data = Arrays.copyOf(data, data.length * 2);
}
data[++top] = obj;
size++;
}
/**
* 出栈
*
* @return 返回栈顶的元素栈顶元素
*/
**public T pop()** {
if (isEmpty()) {
// 栈为空时抛出异常
throw new RuntimeException("栈为空");
} else {
Object obj = data[top--];
size--;
data[size] = null; // help GC
**return (T) obj;**
}
}
/**
* 查看栈顶元素,但并不出栈
*
* @return 返回栈顶的元素
*/
**public T peek()** {
if (isEmpty()) {
// 栈为空时抛出异常
throw new RuntimeException("栈为空");
} else {
**return (T) data[top];**
}
}
/**
* 判断栈是否为空
*
* @return 为空返回true,否则false
*/
public boolean isEmpty() {
return top == -1 ? true : false;
}
/**
* 返回栈中元素的个数
*
* @return 栈中元素的个数
*/
public int size() {
return this.size;
}
}
测试如下:
public class Test {
public static void main(String[] args) {
// 指定栈只能存储int数据
OrderStack<Integer> orderStack = new OrderStack<Integer>();
orderStack.push(10);
orderStack.push(20);
orderStack.push("abc"); // 此行代码将会报错,数据类型不匹配
}
}
好了,到此最基本顺序栈已经差不多完成了,有什么问题欢迎指正。