Java中的Stack类
Java集合框架提供了一个Stack类,用于模拟和实现一个 堆栈数据结构 。该类基于后进先出的基本原则。除了基本的推和弹操作外,该类还提供了三个更多的功能:empty、search和peek。该类也可以说是扩展了Vector并将该类作为堆栈使用五个提到的功能。该类也可以称为Vector的子类。
下面的图表展示了 Stack类的层次结构 :

该类支持一个 默认构造函数Stack() ,用于创建一个空栈。
声明:
public class Stack<E> extends Vector<E>
所有实现的接口:
- 可序列化: 这是一个标记接口,如果要进行序列化和反序列化,类必须实现该接口。
- 可克隆: 这是Java中的一个接口,需要由一个类实现,以允许其对象克隆。
- **可迭代
: ** 这个接口表示一个可迭代的对象集合,也就是可以迭代的对象集合。 - **集合
: ** 集合代表其元素的一组对象。Collection接口用于传递希望实现最大通用性的对象集合。 - **列表
: ** 列表接口提供了一种存储有序的集合的方法。它是Collection的子接口。 - RandomAccess: 这是由List实现使用的标记接口,用于指示它们支持快速(通常是恒定时间)的随机访问。
如何创建一个Stack?
为了创建一个堆栈,我们必须导入 java.util.stack 包并使用该类的Stack()构造函数。下面的例子创建了一个空的Stack。
Stack<E> stack = new Stack<E>();
这里E是Object的类型。
例子:
// 栈实现的Java代码
import java.io.*;
import java.util.*;
class Test
{
// 将元素推到栈的顶部
static void stack_push(Stack<Integer> stack)
{
for(int i = 0; i < 5; i++)
{
stack.push(i);
}
}
// 从栈的顶部弹出元素
static void stack_pop(Stack<Integer> stack)
{
System.out.println("Pop Operation:");
for(int i = 0; i < 5; i++)
{
Integer y = (Integer) stack.pop();
System.out.println(y);
}
}
// 显示栈顶元素
static void stack_peek(Stack<Integer> stack)
{
Integer element = (Integer) stack.peek();
System.out.println("Element on stack top: " + element);
}
// 在栈中查找元素
static void stack_search(Stack<Integer> stack, int element)
{
Integer pos = (Integer) stack.search(element);
if(pos == -1)
System.out.println("Element not found");
else
System.out.println("Element is found at position: " + pos);
}
public static void main (String[] args)
{
Stack<Integer> stack = new Stack<Integer>();
stack_push(stack);
stack_pop(stack);
stack_push(stack);
stack_peek(stack);
stack_search(stack, 2);
stack_search(stack, 6);
}
}
输出:
Pop Operation:
4
3
2
1
0
Element on stack top: 4
Element is found at position: 3
Element not found
在Stack类上执行各种操作
1. 添加元素: 为了向栈中添加元素,我们可以使用 push()方法 这个 push() 操作将元素放在栈的顶部。
// 在栈中添加元素的Java程序
import java.io.*;
import java.util.*;
class StackDemo {
// 主方法
public static void main(String[] args)
{
// 默认初始化栈
Stack stack1 = new Stack();
// 使用泛型初始化栈
Stack<String> stack2 = new Stack<String>();
// 添加元素
stack1.push("4");
stack1.push("All");
stack1.push("Geeks");
stack2.push("Geeks");
stack2.push("For");
stack2.push("Geeks");
// 打印栈元素
System.out.println(stack1);
System.out.println(stack2);
}
}
输出:
[4, All, Geeks]
[Geeks, For, Geeks]
2. 访问元素: 要检索或获取栈的第一个元素或者位于栈顶的元素,我们可以使用 peek() 方法。检索到的元素不会从栈中删除。
// Java 程序演示如何从栈中访问元素
import java.util.*;
import java.io.*;
public class StackDemo {
// 主方法
public static void main(String args[])
{
// 创建一个空栈
Stack<String> stack = new Stack<String>();
// 使用 push() 方法将元素添加到栈中
stack.push("Welcome");
stack.push("To");
stack.push("Geeks");
stack.push("For");
stack.push("Geeks");
// 显示栈
System.out.println("Initial Stack: " + stack);
// 从栈顶取出元素
System.out.println("The element at the top of the"
+ " stack is: " + stack.peek());
// 显示修改后的栈
System.out.println("Final Stack: " + stack);
}
}
输出结果:
Initial Stack: [Welcome, To, Geeks, For, Geeks]
The element at the top of the stack is: Geeks
Final Stack: [Welcome, To, Geeks, For, Geeks]
3. 移除元素: 为了从栈中弹出元素,我们可以使用 pop() 方法。该方法从栈的顶部弹出元素并将其删除。
// Java 程序演示如何从栈中移除元素
import java.util.*;
import java.io.*;
public class StackDemo {
public static void main(String args[])
{
// 创建一个空栈
Stack<Integer> stack = new Stack<Integer>();
// 使用 add() 方法添加元素
stack.push(10);
stack.push(15);
stack.push(30);
stack.push(20);
stack.push(5);
// 显示栈
System.out.println("Initial Stack: " + stack);
// 使用 pop() 方法移除元素
System.out.println("Popped element: "
+ stack.pop());
System.out.println("Popped element: "
+ stack.pop());
// 显示修改后的栈
System.out.println("Stack after pop operation "
+ stack);
}
}
输出结果:
Initial Stack: [10, 15, 30, 20, 5]
Popped element: 5
Popped element: 20
Stack after pop operation [10, 15, 30]
示例
在 Java 中,Stack 类是 Vector 类的子类,代表对象的后进先出(LIFO)堆栈。 它扩展了 Vector 类,以便轻松实现栈数据结构。
下面是如何在 Java 中使用 Stack 类的示例:
import java.util.Stack;
public class StackExample {
public static void main(String[] args) {
// 创建一个新的栈
Stack<Integer> stack = new Stack<>();
// 将元素推入栈中
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
// 从栈中弹出元素
while(!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
输出结果:
4
3
2
1
在此示例中,我们首先从 java.util 包中导入 Stack 类。然后使用默认构造函数创建一个名为 stack 的新 Stack 对象。使用 push() 方法将四个整数推入栈中。然后在 while 循环中使用 pop() 方法从栈中弹出元素。在尝试弹出元素之前,使用 isEmpty() 方法检查栈是否为空。
这段代码创建了一个整数类型的栈(Stack)数据结构,并按顺序将4个整数压入栈中,顺序为1->2->3->4。然后我们使用pop()方法逐个从栈中弹出元素,该方法会移除并返回栈顶的元素。由于栈遵循后进先出(LIFO)的顺序,元素会以插入的相反顺序被弹出,从而得到上述输出结果。
Stack类提供了多个其他的方法来操作栈,如peek()方法可以获取栈顶元素但不会移除它,search()方法可以在栈中查找元素并返回它的位置,size()方法可以返回栈的当前大小。Stack类还提供了多个构造函数,用于创建具有指定初始容量或复制现有栈的堆栈。
Stack类中的方法
| 方法 | 描述 |
|---|---|
| empty() | 如果栈顶没有任何元素,则返回true,否则返回false。 |
| peek() | 返回栈顶元素,但不移除它。 |
| pop() | 移除并返回栈顶元素。如果调用pop()时调用的栈为空,则抛出“EmptyStackException”异常。 |
| push(Object element) | 将一个元素推入栈顶。 |
| search(Object element) | 确定对象是否存在于栈中。如果找到该元素,则返回从栈顶计算的元素位置;否则返回-1。 |
从java.util.Vector类继承的方法
| 方法 | 描述 |
|---|---|
| add(Object obj) | 将指定元素添加到此向量的末尾。 |
| add(int index, Object obj) | 在此向量的指定位置插入指定元素。 |
| addAll(Collection c) | 将指定集合中的所有元素以指定集合的迭代器返回的顺序添加到此向量的末尾。 |
| addAll(int index, Collection c) | 将指定集合中的所有元素插入到此向量的指定位置。 |
| addElement(Object o) | 将指定的组件添加到此向量的末尾,将向量的大小增加1。 |
| capacity() | 返回此向量当前的容量。 |
| clear() | 从此向量中删除所有元素。 |
| clone() | 返回此向量的克隆。 |
| contains(Object o) | 如果此向量包含指定的元素,则返回true。 |
| containsAll(Collection c) | 如果此向量包含指定集合中的所有元素,则返回true。 |
| copyInto(Object []array) | 将此向量的组件复制到指定的数组中。 |
| elementAt(int index) | 返回指定索引处的组件。 |
| elements() | 返回此向量的组件的枚举。 |
| ensureCapacity(int minCapacity) | 如有必要,增加此向量的容量,以确保它至少可以包含由最小容量参数指定的组件数。 |
| equals() | 将指定对象与此向量比较以测试相等性。 |
| firstElement() | 返回向量的第一个组件(索引为0的项)。 |
| get(int index) | 返回此向量中指定位置的元素。 |
| hashCode() | 返回此向量的哈希代码值。 |
| indexOf(Object o) | 返回此向量中指定元素的第一次出现的索引,如果此向量不包含该元素,则返回-1。 |
| indexOf(Object o, int index) | 返回从索引开始向前搜索指定元素在此向量中第一次出现的索引,如果未找到该元素,则返回-1。 |
| insertElementAt(Object o, int index) | 将指定对象作为组件插入到此向量中的指定索引处。 |
| isEmpty() | 如果此向量没有组件,则返回true。 |
| iterator() | 返回列表中元素的迭代器,以正确的顺序。 |
| lastElement() | 返回向量的最后一个组件。 |
| lastIndexOf(Object o) | 返回此向量中指定元素的最后一次出现的索引,如果此向量不包含该元素,则返回-1。 |
| lastIndexOf(Object o, int index) | 返回从索引开始向后搜索指定元素在此向量中最后一次出现的索引,如果未找到该元素,则返回-1。 |
| remove(int index) | 删除此向量中指定位置的元素。 |
| remove(Object o) | 从此向量中删除第一次出现的指定元素(如果存在)。 |
| removeAll(Collection c) | 从此向量中删除包含在指定集合中的所有元素。 |
| removeElement(Object o) | 从此向量中删除指定元素的第一个(最低索引)出现。 |
| removeElementAt(int index) | 删除指定索引处的组件。 |
| retainAll(Collection c) | 在此向量中仅保留包含在指定集合中的元素。 |
| set(int index, Object element) | 将指定索引处的组件设置为指定的对象。 |
| setElementAt(Object o, int index) | 将指定索引处的组件设置为指定的对象。 |
| setSize(int newSize) | 设置此向量的大小。 |
| size() | 返回此向量中的组件数。 |
| subList(int fromIndex, int toIndex) | 返回此向量的部分视图,其组件范围从指定的fromIndex(含)到指定的toIndex(不含)。 |
| toArray() | 返回包含此向量中所有元素的数组。 |
| toArray(T[] a) | 返回包含此向量中所有元素的数组;返回数组的运行时类型是指定数组的类型。 |
| toString() | 返回此向量的字符串表示形式,其中包含每个元素的字符串表示形式。 |
| trimToSize() | 将此向量的容量调整为向量当前大小,即删除所有容量多余的元素。 |
| lastIndexOf(Object o, int index) | 返回指定元素在向量中从指定索引开始反向搜索的最后一个出现位置的索引;如果未找到该元素则返回 -1。 |
| listIterator() | 返回此列表中按适当顺序的元素上的列表迭代器。 |
| listIterator(int index) | 从列表中的指定位置开始,返回按适当顺序的元素上的列表迭代器。 |
| remove(int index) | 删除向量中指定位置的元素。 |
| remove(Object o) | 如果向量中存在指定的元素,则删除第一个出现的指定元素。如果向量不包含元素,则不做任何操作。 |
| removeAll(Collection c) | 从此向量中删除包含在指定集合中的所有元素。 |
| removeAllElements() | 删除此向量中的所有组件并将其大小设置为零。 |
| removeElement(Object o) | 从此向量中删除第一个(最低索引)出现的参数。 |
| removeElementAt(int index) | 删除指定索引处的组件。 |
| removeRange(int fromIndex, int toIndex) | 删除索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。 |
| retainAll(Collection c) | 仅保留此向量中包含在指定集合中的元素。 |
| set(int index, Object o) | 在向量的指定位置替换指定元素。 |
| setElementAt(Object o, int index) | 将此向量中指定索引处的组件设置为指定的对象。 |
| setSize(int newSize) | 设置此向量的大小。 |
| size() | 返回此向量中的组件数。 |
| subList(int fromIndex, int toIndex) | 返回列表中在 fromIndex(包括)和 toIndex(不包括)之间的部分的视图。 |
| toArray() | 按正确顺序返回包含此向量中所有元素的数组。 |
| toArray(Object []array) | 按正确顺序返回包含此向量中所有元素的数组;返回数组的运行时类型是指定数组的类型。 |
| toString() | 返回包含每个元素的字符串表示形式的此向量的字符串表示形式。 |
| trimToSize() | 将此向量的容量修剪为向量的当前大小。 |
优先使用Deque而不是Stack -:
Java中的Stack类是一个遗留类,继承自Java中的Vector。它是一个线程安全类,因此在不需要线程安全时会涉及开销。建议在单线程环境下使用ArrayDeque进行栈实现,因为它更有效率。
// 一个Java程序,展示了使用ArrayDeque实现栈的方法
import java.util.*;
class GFG {
public static void main (String[] args) {
Deque<Character> stack = new ArrayDeque<Character>();
stack.push('A');
stack.push('B');
System.out.println(stack.peek());
System.out.println(stack.pop());
}
}
输出结果:
B
B
使用Deque替代Stack的另一个原因是,Deque具有使用流将LIFO概念应用到列表中的能力,而Stack则没有。
import java.util.*;
import java.util.stream.Collectors;
class GFG {
public static void main (String[] args) {
Stack<Integer> stack = new Stack<>();
Deque<Integer> deque = new ArrayDeque<>();
stack.push(1);//1是顶部
deque.push(1);//1是顶部
stack.push(2);//2是顶部
deque.push(2);//2是顶部
List<Integer> list1 = stack.stream().collect(Collectors.toList());//[1,2]
System.out.println("Using Stack -");
for(int i = 0; i < list1.size(); i++){
System.out.print(list1.get(i) + " " );
}
System.out.println();
List<Integer> list2 = deque.stream().collect(Collectors.toList());//[2,1]
System.out.println("Using Deque -");
for(int i = 0; i < list2.size(); i++){
System.out.print(list2.get(i) + " " );
}
System.out.println();
}
}
输出结果:
Using Stack -
1 2
Using Deque -
2 1
示例:
在Java中,静态类是使用static修饰符声明的嵌套类。静态类只能访问其外部类的静态成员,但可以在不创建外部类的实例的情况下实例化它。
以下是Java中静态类的示例:
public class MyOuterClass {
private static int count = 0;
public static class MyStaticClass {
private int id;
public MyStaticClass() {
this.id = ++count;
}
public void printId() {
System.out.println("MyStaticClass id: " + this.id);
}
}
public static void main(String[] args) {
MyStaticClass static1 = new MyStaticClass();
MyStaticClass static2 = new MyStaticClass();
static1.printId(); // 输出"MyStaticClass id: 1"
static2.printId(); // 输出"MyStaticClass id: 2"
}
}
输出结果:
MyStaticClass id: 1
MyStaticClass id: 2
在这个例子中,MyOuterClass包含一个名为MyStaticClass的静态嵌套类。MyStaticClass有一个名为id的实例字段,每次创建MyStaticClass的新实例时都会初始化为唯一值。MyStaticClass还有一个名为printId()的方法,用于打印id的值。 在MyOuterClass的main()方法中,我们创建了两个MyStaticClass的实例,并在每个实例上调用printId()方法。输出结果表明,每个实例都有一个唯一的id值。
- 请注意,由于MyStaticClass被声明为静态,我们可以创建它的实例而不创建MyOuterClass的实例。这与非静态嵌套类不同,非静态嵌套类只能使用其外部类的实例进行实例化。
静态类在我们想要封装与特定类紧密相关但不依赖于该类的任何特定实例的功能时非常有用。它们还可以帮助组织代码并减少为小型相关功能创建新类的混乱。
Java中静态类的实时用途
在Java中,静态类是指只有静态方法和字段且无法实例化的类。以下是Java中静态类的一些实时用例:
实用程序类: Java中静态类的常见用例是创建仅包含静态方法且用于执行应用程序中常见操作的实用程序类。例如,java.util.Collections类是一个静态类,其中包含用于执行诸如排序、搜索和洗牌等多种操作的方法。
数学函数: Java中静态类的另一个常见用法是定义应用程序中使用的数学函数。例如,java.lang.Math类是一个静态类,提供用于执行三角函数、指数函数和对数函数等数学运算的方法。
常量: 静态类还用于定义在应用程序中使用的常量。例如,java.awt.Color类是一个静态类,定义各种颜色作为静态常量,可以在应用程序中使用。
单例模式: 单例模式是一种设计模式,用于确保在应用程序中只创建类的一个实例。实现单例模式的一种方法是使用具有私有构造函数和返回类单一实例的静态实例变量的静态类。
辅助类: 静态类通常用作辅助类,以提供应用程序中使用的常见功能。例如,java.text.NumberFormat类是一个静态类,提供格式化和解析数字的方法。
总之,Java中的静态类对于创建实用程序类、定义数学函数和常量、实现单例模式、以及作为帮助类为应用程序提供通用功能等方面非常有用。
静态类的优势:
内存效率: 静态类只在应用程序生存期内加载到内存中一次,因此比非静态类更具内存效率。
容易访问: 由于静态类在创建类的任何对象之前加载到内存中,因此可以在不创建类的实例的情况下轻松访问它们。
实用程序类: 静态类特别适用于创建不需要实例化的实用程序类,例如Math类。
全局访问能力: 静态类可以全局访问,这意味着它们可以从应用程序中的任何地方访问。
静态类的缺点:
难以测试: 由于静态类无法实例化,因此使用自动化单元测试对其进行测试可能会很困难。
紧密耦合: 静态类可以导致类之间的紧密耦合,这可能会使代码更难以维护和修改。
扩展困难: 静态类无法扩展或子类化,这可能会限制代码的灵活性。
灵活性受限: 与非静态类相比,静态类在灵活性方面受到限制,因为它们无法实现接口或用于多态的上下文中。
需要注意的是,应谨慎使用静态类,在其优势超过其劣势时才使用。
极客教程