登录 |  注册
首页 >  面试合集 >  Java面试宝典(第三部分·高级) >  JVM队列和栈是什么?有什么区别?

JVM队列和栈是什么?有什么区别?

队列和栈都是被用来预存储数据的。

  • 操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。

  • 可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。

  • 操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。

JAVA数据类型-栈与队列

一. 栈(Stack)

栈是一种只允许在一端进行插入或删除的线性表,一般为先进后出(LIFO)

1、栈的操作端通常被称为栈顶,另一端被称为栈底。

2、栈的插入操作称为进栈(压栈|push);栈删除操作称为出栈(弹栈|pop)。

对于栈我是这么理解,大家应该都打过羽毛球,羽毛球盒里的球都是叠在一起的,当我们放进一颗羽毛球时肯定是叠在最上面,下次我们要用的时候拿出来还是从这个最上面的取,这种就类似栈,采用先进后出规则。

//看下源码;
public class Stack<E>extends Vector<E>

Stack类继承了Vector类,它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。

主要方法:

booleanempty() 测试堆栈是否为空。

Epeek() 查看堆栈顶部的对象,但不从堆栈中移除它。

Epop() 移除堆栈顶部的对象,并作为此函数的值返回该对象。

Epush(E item) 把项压入堆栈顶部。

intsearch(Object o) 返回对象在堆栈中的位置,以 1 为基数。

栈的实现(基于数组):

public class Stack{
    private final int count;
    private String[] stackStr;
    private int ptr = -1;
    public Stack(){
        ptr = -1;
        this.count = 100;
        stackStr = new String[this.count];
    }
    public Stack(int count){
        this.count = count;
        ptr = -1;
        stackStr = new String[this.count];
    }
    public void push(String s) throws ArrayIndexOutOfBoundsException{
        if (ptr < count - 1){
            stackStr[++ptr] = s;
        }
        else 
            System.out.print(s + " 压栈溢出! \n"); 
    }
    public String pop(){
        String s = stackStr[ptr];
        ptr--;
        return s;
    }
    public Boolean isEmpty(){
        if (ptr == -1)
            return true;
        else 
            return false;
    }
    public String toString(){
        String s = "";
        for(int i = 0; i <= ptr; i++){
            s = s + stackStr[i] + " ";
        }
        return s;
    }
    
    public static void main(String[] args){
        Stack stk = new Stack(2);
        stk.push("a");
        stk.push("b");
        stk.push("c");
        print(stk.toString());
        print("\n");
        stk.pop();
        print(stk.toString());
    }
    static void print(String s){
        System.out.print(s);
    }
}

Java栈与堆

1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

3.我们知道Java中的数据类型有两种。 一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。 

   另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义 

   int a = 3; 

   int b = 3; 

   编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。 

   特别注意的是,这种字面值的引用与类对象的引用不同。通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。 然而对于类对象的引用则不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化,我们可以看下下面这一段代码:

public class Test {
private String id="";
public String getId() {
    return id;
}
public void setId(String id) {
    this.id = id;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
private String name="";
public static void main(String[] args) {
    Test test=new Test();
    test.setId("1");
    test.setName("cc");
    changeId(test);
    System.out.println(test.getId());
}
public static void changeId(Test test)
{
    Test tt=new Test();
    //tt=test;   如删去此处
    tt.setId("5");
    System.out.println(tt.getId());
}
}

很明显执行上段代码结果是输出

5
1 

但如果删去注释符号//,使tt和test指向同一个对象,则此时运行结果为:

5
5

另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

每个JVM的线程都有自己的私有的栈空间,随线程创建而创建,java的stack存放的是frames ,java的stack和c的不同,只是存放本地变量,返回值和调用方法,不允许直接push和pop frames ,因为frames 可能是有heap分配的,所以j为ava的stack分配的内存不需要是连续的。java的heap是所有线程共享的,堆存放所有 runtime data ,里面是所有的对象实例和数组,heap是JVM启动时创建。

二. 队列(Queue):

什么是队列?

队列是一种常用的数据结构。队列Queue接口与List、Set同一级别,都是实现了Collection接口。在处理元素前用于保存元素的 collection。

这里看下定义:

队列是一种特殊的线性表,是一种先进先出(FIFO)的数据结构。它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。

结合生活很好理解,顾名思义,就像排队一样,正常情况下,先排的人先走,后排的人后走。而且根据FIFO规则,所有新元素都添加在末尾,对列表的头进行删除操作。

此外,我们熟悉的LinkedList,继承AbstractSequentialList<E>,实现List<E>, Deque<E>, Cloneable, java.io.Serializable等的接口

阻塞队列:BlockingQueue

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。

在并发编程中,有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法。

五个阻塞队列类:

  • ArrayBlockingQueue:一个由数组支持的有界队列。

  • LinkedBlockingQueue:一个由链接节点支持的可选有界队列。

  • PriorityBlockingQueue:一个由优先级堆支持的无界优先级队列。

  • DelayQueue:一个由优先级堆支持的、基于时间的调度队列。

  • SynchronousQueue:一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。

其方法有如下:

  • add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常

  • remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常

  • element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常

  • offer 添加一个元素并返回true 如果队列已满,则返回false

  • poll 移除并返问队列头部的元素 如果队列为空,则返回null

  • peek 返回队列头部的元素 如果队列为空,则返回null

  • put 添加一个元素 如果队列满,则阻塞

  • take 移除并返回队列头部的元素 如果队列为空,则阻塞

阻塞队列提供了可阻塞的put和take方法,它们与可定时的offer和poll是等价的。如果Queue已经满了,put方法会被阻塞直到有可用的空间;如果Queue是空的,那么take方法会被阻塞,直到有元素可用。Queue的长度可以有限,也可以无限;无限的Queue永远不会充满,所以它的put方法永远不会阻塞。

注意:poll和peek方法出错进返回null。因此,向队列中插入null值是不合法的

上一篇: JVM堆栈的区别
下一篇: JVM浅谈双亲委派模型
推荐文章
  • 在HTML中,如果你想让一个输入框(input元素)不可编辑,你可以通过设置其readonly属性来实现。示例如下:input type="text" value="此处内容不可编辑" readonly在上述代码中,readonly属性使得用户无法修改输入框中的内容。另外,如果你希望输入框完全不可交
  • ASP.NET教程ASP.NET又称为ASP+,基于.NETFramework的Web开发平台,是微软公司推出的新一代脚本语言。ASP.NET是一个使用HTML、CSS、JavaScript和服务器脚本创建网页和网站的开发框架。ASP.NET支持三种不一样的开发模式:WebPages(Web页面)、
  • C# 判断判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。下面是大多数编程语言中典型的判断结构的通常形式:判断语句C#提供了以下类型的判断语句。点击链接查看每个语句的细节。语句描述if语句一个 if语句 由一个布尔表达式后跟
  • C#循环有的时候,可能需要多次执行同一块代码。通常情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。编程语言提供了允许更为复杂的执行路径的多种控制结构。循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的通常形式:循环类型C#提供了以下几种循环类型
  • C#数组(Array)数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合,一般认为数组是一个同一类型变量的集合。声明数组变量并不是声明number0、number1、...、number99一个个单独的变量,而是声明一个就像numbers这样的变量,然后使用numbers[0]
  • ASP.NET是一个由微软公司开发的用于构建Web应用程序的框架,它是.NETFramework的一部分。它提供了一种模型-视图-控制器(MVC)架构、Web表单以及最新的ASP.NETCore中的RazorPages等多种开发模式,可以用来创建动态网页和Web服务。以下是一些基础的ASP.NET编
学习大纲