`
clearity
  • 浏览: 36905 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java堆栈数据结构的问与答

 
阅读更多

Java的集合类库是最常用的类库之一。栈的后进先出机制可以在很多地方派上用场,比如,表达式预估/语法解析,验证和解析XML,还原文本编辑器的内容,web浏览器的页面访问记录等等。下面是一些关于栈的知识。

 

Q.Java中有什么后入先出的实现可以使用?

A.向量是栈的传统实现并且Java文档中规定使用Deque来代替是因为提供了更好的后进先出的操作支持。Deque的实现通常都比Stack的要好。J2SE5引入了Queue接口,并且加入了实现Queue接口的Deque。Deque接口应该被读作“deck”而不是“de-queue”,并且在从queue中移除元素也不会被认为是dequeue。双端队列支持从任何一端进行元素的增加和删除,因此它可以被用作队列(先进先出/FIFO)或者栈(后进先出LIFO).

 

注意:在它之前的Vector向量类使用的是内部同步,在数据一致性上很差劲,不恰当的使用会让性能大打折扣。新引入的java.util.concurrent包提供了更有效的线程安全的实现。

 

Q.让你在指定的字符串里匹配开括号的对应的闭括号,应该怎么实现?

A.首先,把实现用伪代码写出来,然后按照伪代码实现

  1.将所有的圆括号存储在栈中。可以保证后进先出的顺序

  2.当遇到一个闭括号时,从栈中取出最后一个压入的元素,这就是对应的开括号

  3.如果没有匹配到,则不存在成对的括号

如果需要,下面这个图描述了这个逻辑。

 



 

 

下面是演示使用栈匹配括号对的例子,其中枚举数据定义了不同的括号类型

 

 

public enum PARENTHESIS {
 
 LP('('), RP(')'), LB('{'), RB('}'), LSB('['), RSB(']');
 
 char symbol;
 
 PARENTHESIS(Character symbol) {
  this.symbol = symbol;
 }
 
 char getSymbol() {
  return this.symbol;
 }
} 

 

 

 

现在,是栈的后进先出特性在开括号匹配闭括号的时候起了作用。如果匹配到左侧开括号就压入栈中,然后查找匹配的闭括号,如果匹配则把对应的开括号从栈中弹出。

 

 

import java.util.ArrayDeque;
import java.util.Deque;
 
public class Evaluate {
 
 //存储括号
 final Deque<character> paranthesesStack = new ArrayDeque<character>();
 
 public boolean isBalanced(String s) {
 
  for (int i = 0; i < s.length(); i++) {
 
   if (s.charAt(i) == PARENTHESIS.LP.getSymbol() || 
       s.charAt(i) == PARENTHESIS.LB.getSymbol() ||
       s.charAt(i) == PARENTHESIS.LSB.getSymbol())
     
     paranthesesStack.push(s.charAt(i));      //自动装箱
                                              // 把开括号压入栈中
 
   //对每一个闭括号都要检查是否在栈中有匹配的开括号
   else if (s.charAt(i) == PARENTHESIS.RP.getSymbol()){  
    if (paranthesesStack.isEmpty() || paranthesesStack.pop() != PARENTHESIS.LP.getSymbol())
     return false;
   }
 
   else if (s.charAt(i) == PARENTHESIS.RB.getSymbol() ) {
    if (paranthesesStack.isEmpty() || paranthesesStack.pop() !=PARENTHESIS.LB.getSymbol() )
     return false;
   }
 
   else if (s.charAt(i) == PARENTHESIS.RSB.getSymbol()) {
    if (paranthesesStack.isEmpty() || paranthesesStack.pop() != PARENTHESIS.LSB.getSymbol())
     return false;
   }
 
  }
   
  return paranthesesStack.isEmpty(); //如果栈中元素全部弹出,则括号全部匹配上
 }
 
}

 

 

 

注意:ArrayDeque不是线程安全的,也不允许保存为null的元素。并发包中有BlockingQueue和LinkedBlockingQueue两种实现。

 

最后,写一个简单的测试用例。需要将junit的jar包放入类路径中,这里使用的版本是4.8.2.

 

 

import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
 
 
public class EvaluateTest {
  
 Evaluate eval = null;
  
 @Before
 public void setUp(){
  eval = new Evaluate();
 }
  
 @Test
 public void testPositiveIsBalanced(){
   boolean result = eval.isBalanced("public static void main(String[] args) {}");
      Assert.assertTrue(result);   
       
 }
  
  
 @Test
 public void testNegativeIsBalanced(){
   boolean result = eval.isBalanced("public static void main(String[ args) {}");   // missing ']'
      Assert.assertFalse(result);
       
      result = eval.isBalanced("public static void main(String[] args) }");          // missing '{'
      Assert.assertFalse(result);
       
       
      result = eval.isBalanced("public static void main String[] args) {}");        // missing '('
      Assert.assertFalse(result);
       
 }
 
}

 

 

 

提示:测试的时候用例要覆盖正负两个方向

 

注意:上面的例子演示了后进先出机制来实现括号成对的匹配。但是事实上的实现远没有如此适合。当需要很大一坨if-else判断或者switch语句的时候,就应该考虑一个面向对象的解决方案了。

 

Q.为什么Deque接口和其他集合接口不同?

A.对于Deque,可以在集合的两端随意的添加或者删除元素。而其他集合却只能在末尾一端进行插入和删除操作

 

Q.程序的执行可以用什么不同的方式来查询跟踪?

A.

  1.Java是基于栈实现的语言,程序的执行伴随着出栈和入栈。当进入一个方法时,会将程序和数据压入栈中,如果方法还要调用很多其他的方法,就会根据方法执行的顺序将数据压入栈中。当方法执行完成后,需要根据后进先出的原则将栈中的数据弹出,比如方法A调用方法B,然后方法B再调用方法C,那么当方法C执行完成后,C方法相关的会先弹出,然后是方法B的,最后才是方法A的。当有异常发生时,会生成方便跟踪异常发生点的堆栈信息并打印出来。

 


                                                  

 
 

 

  2.Java程序员随时都可以拿到堆栈信息。调用方式如下

 

 

Thread.currentThread().getStackTrace() ; //获取堆栈信息

 

 

 

可以使用Java工具箱中的jstack之类的工具来获取线程的堆栈信息,其他工具还有Jconsole,还或者在Win32平台上使用CTRL+BREAK组合键、在Posix操作系统上通过信号Kill -quit生成线程堆栈转储信息。你还可以使用下面代码的方式使用JMX API获取。ThreadMXBean是JVM线程子系统的管理接口。

 

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(true, true);
for (ThreadInfo info : infos) {  
    StackTraceElement[] elems = info.getStackTrace();  
 //TODO
}

 

线程堆栈转出信息对于寻找比如死锁、资源争用、线程饥饿等并发问题很有用。

 

Q.Java中有递归的方法调用吗?

A.答案是确定的,Java是基于栈的并且是借助的后进先出的特性,它会记住调用者的位置,所以当方法返回的时候知道将结果返回到正确的地方。

 

Q.你怎么正确的分析堆栈跟踪信息?

A.

  1.堆栈信息需要正确理解的一个最重要的概念是它显示的是按照操作执行的时间排序的路径。也就是它的后进先出特性。

  2.下面是一段很简单的堆栈信息,它可以告诉你的是在代码类C的16行上发生了空指针异常。可以从最上面的类中看到。

 

Exception in thread "main" java.lang.NullPointerException
        at com.myapp.ClassC.methodC(ClassC.java:16)
        at com.myapp.ClassB.methodB(ClassB.java:25)
        at com.myapp.ClassA.main(ClassA.java:14)

 

3.包含多个“caused by”段的堆栈信息会显得更复杂,在当前例子中你可以从最底部的“caused by”看到最根本的原因,例如,

 

Exception in thread "main" java.lang.IllegalStateException: ClassC has a null property
        at com.myapp.ClassC.methodC(ClassC.java:16)
        at com.myapp.ClassB.methodB(ClassB.java:25)
        at com.myapp.ClassA.main(ClassA.java:14)
Caused by: com.myapp.MyAppValidationException
        at com.myapp.ClassB.methodB(ClassB.java:25)
        at com.myapp.ClassC.methodC(ClassC.java:16)
        ... 1 more
Caused by: java.lang.NullPointerException
        at com.myapp.ClassC.methodC(ClassC.java:16)
        ... 1 more

 

最根本的原因是由最后一个“caused by”也就是类C的第16行代码出抛出的空指针异常

 

4.当你使用过多的第三方类库比如Spring、Hibernate等等,显示在你面前的堆栈异常会有很多,这时你需要查看最底部的“caused by”段,那里是才是异常发生的地方。

 

Q.怎样反向输出数组{1,4,6,7,8,9}?

A.有很多方式可以实现。谈到LIFO,下面的例子演示了使用栈来实现的代码

 

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
 
 
public class ReverseNumbers {
 
 public static void main(String[] args) {
  Integer[] values = {1, 4, 6, 7, 8, 9};
  Deque<integer> numberStack = new ArrayDeque<integer>(10);
   
  for (int i = 0; i < values.length; i++) {
   numberStack.push(values[i]);                   // 按照给定的顺序压入栈中
  }
   
  Integer[] valuesReversed = new Integer[values.length];
   
  int i = 0;
  while (!numberStack.isEmpty()) {
   valuesReversed[i++] = numberStack.pop(); //反向顺序弹出
                                            // i++ is a post increment i.e.
                                            // assign it and then increment it 
                                            // for the next round 
  }
   
  System.out.println(Arrays.deepToString(valuesReversed));
   
 }
}

 

 

输出结果如下

 

   

[9, 8, 7, 6, 4, 1]

 

  • 大小: 26.5 KB
  • 大小: 4.8 KB
分享到:
评论

相关推荐

    Java数组堆栈

    Java数组堆栈是指使用Java编程语言实现的基于数组的堆栈数据结构。该数据结构提供了基本的堆栈操作,如push、pop、peek、isEmpty、exist等方法。下面是对Java数组堆栈的详细解释。 标题: Java数组堆栈 描述: 文章...

    数据结构 C C++ JAVA

    C、C++ 和 Java 都是广泛用于实现数据结构的编程语言,每种语言都有其独特的特性和优势。 在C语言中,由于其低级特性,可以直接对内存进行操作,这使得C语言在实现数据结构时更加灵活,但同时也需要开发者具有较高...

    常用数据结构(堆栈,队列,列表)JAVA代码

    在这个主题中,我们将深入探讨Java实现的三种基本数据结构:堆栈(Stack)、队列(Queue)和列表(List)。这些概念是计算机科学的核心部分,对理解和解决复杂问题至关重要。 1. **堆栈(Stack)**: - 堆栈是一种...

    java 堆栈的演示程序

    `Stack`类是Java集合框架的一部分,提供了push、pop、peek等方法来操作元素,这与JVM内部的堆栈机制有相似之处,但请注意,`Stack`类并不等同于JVM的堆栈,它更多地是一种数据结构的实现。 通过分析这个演示程序,...

    java语言数据结构

    Java语言数据结构是编程学习中的重要一环,它涉及到如何高效地存储和处理数据,是所有Java开发者都应该掌握的基础知识。这个压缩包包含了多个章节的源代码,可以帮助学习者深入理解数据结构的实现细节。 首先,让...

    清华邓俊辉Java数据结构

    《清华邓俊辉Java数据结构》是一门深入探讨数据结构及其在Java编程语言中实现的课程。这门课程由清华大学的邓俊辉教授主讲,旨在帮助学生掌握数据结构的基本概念,理解它们的工作原理,并能用Java语言进行实际操作。...

    数据结构-堆栈及其应用-Java代码实现

    数据结构是计算机科学中至关重要的概念,而堆栈(Stack)作为其中的一种基本结构,具有“后进先出”(LIFO,Last In First Out)的特性,广泛应用于各种算法和程序设计中。本主题主要关注Java语言实现的堆栈及其在...

    数据结构之堆栈的使用

    总结来说,本程序通过堆栈数据结构实现了十进制数到八进制数的转换,展示了堆栈在算法设计中的实用性。理解和掌握堆栈不仅可以帮助我们编写更高效的代码,也是深入学习计算机科学基础的重要一步。

    数据结构代码例题 堆栈 树 连表 字符串 基本操作程序员笔试必备的东西

    堆栈是一种“后进先出”(LIFO)的数据结构,常用于实现函数调用、表达式求值等场景。堆栈的基本操作包括压栈(Push)、弹栈(Pop)、查看栈顶元素(Peek)和检查栈是否为空(IsEmpty)。C/C++中可以使用数组或链表...

    数据结构-Java中实现一个简单的堆栈结构Stack

    数据结构-Java中实现一个简单的堆栈结构Stack,通过数组实现堆栈数据结构,简单易懂,适合数据结构初学者学习理解堆栈的原理实现。

    堆栈实现的java计算器

    通过这个课程设计,不仅可以巩固堆栈数据结构的理解,还能提升对字符串处理、条件判断和逻辑控制的掌握,这些都是编程中不可或缺的技能。同时,这也是一个很好的实践机会,将所学理论知识转化为实际代码,从而加深对...

    用 Java 实现堆栈

    在计算机科学中,堆栈是一种基础且重要的数据结构,它遵循“后进先出”(LIFO)的原则。Java作为一种广泛使用的编程语言,提供了多种方式来实现堆栈,包括使用数组、链表以及内置的java.util.Stack类。下面我们将...

    java的堆栈,很详细的资料

    Java 堆栈是 Java 中的一种基本数据结构,它是 JVM(Java Virtual Machine)为每个新创建的线程分配的。堆栈以帧为单位保存线程的状态,JVM 对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。 Java 堆栈的结构...

    数据结构与算法——堆栈实现括号匹配

    数据结构与算法是计算机科学的基础,它们在编程和软件开发中扮演着至关重要的角色。堆栈(Stack)作为其中一种简单但实用的数据结构,广泛应用于括号匹配问题,例如在编译器、解释器和文本编辑器中。在这个实验中,...

    数据结构—Java语言描述(朱战立版)课件及源代码

    朱战立版的《数据结构—Java语言描述》是一本深入浅出地讲解数据结构的教材,它以Java编程语言为载体,帮助读者理解和实现各种经典的数据结构。Java作为一种多用途、面向对象的语言,其强大的抽象能力和丰富的类库...

    堆栈算法的JAVA迷宫

    在这个“堆栈算法的JAVA迷宫”项目中,我们将深入探讨如何利用Java编程语言和堆栈数据结构来解决迷宫问题,并创建一个具有交互界面的程序。 首先,堆栈是一种后进先出(LIFO)的数据结构,适用于处理需要回溯的问题...

    数据结构(JAVA版)教学课件

    在本套JAVA版的数据结构教学课件中,我们将深入学习几种关键的数据结构及其在JAVA编程中的实现。以下是对各章节内容的详细解读: 第一章:这部分通常会介绍数据结构的基本概念,包括什么是数据结构,它的重要性以及...

    图解数据结构--使用Java

    全书内容浅显易懂,利用大量且丰富的图示与范例, 详解复杂的抽象理论,从最基本的数据结构概念开始 说明,再以Java工具加以诠释阵列结构、堆栈、链表 、队列、排序、查找等重要的概念,引领读者抓住重 点轻松进入...

    Java数据结构与算法书中源码

    针对提供的文件列表,我们可以深入探讨以下几个Java数据结构与算法的知识点: 1. **字典树(Trie)** - 尽管未直接提供字典树的源码,但`SuffixArray.java`可能涉及到了字典树的概念。字典树是一种用于快速查找字符...

Global site tag (gtag.js) - Google Analytics