java中堆栈区别,递归和迭代区别
来自:http://bbs.byr.cn/#!article/buptAUTA/31
1. 读取某个文件夹下的文件
采用深度优先的方法,遍历文件夹,有文件就进行文件操作。
深度优点方法:使用递归实现;
private void recursion (Path path) { FileStatus[] children = fs.listStatus (path); for(FileStatus child : children){ if(child.isDir()){ recursion(child.getPath()); } else{ …… //执行文件处理代码 } } }
注意:当路径深度很少的时候这样的方法不存在问题,但是如果路径大于100+,就会出现栈溢出的错误。
Why?
换成迭代实现
Stack<FileStatus> pathstack = new Stack<FileStatus>(); for(pathstack.push(fs.getFileStatus(path)); !pathstack.empty();){ FileStatus cur = pathstack.pop(); FileStatus[] children = fs.listStatus(cur.getPath()); for(int i = 0; i < children.length; i++) { final FileStatus child = children[i]; if (child.isDir()) { pathstack.push(child); } else { …… //执行文件处理代码 } } }
问题消失了~~Why?
2. 堆和栈的区别
堆是有序完全二叉树,栈是一种先进后出的线性表,栈的特点是速度快,jvm的压栈和出栈操作都是非常高效的(相对来说,堆的二叉树遍历是要比先进后出线性表要慢的)。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆,对象的大小是不可估计的,是动态的。
对象的应用却放在栈中,一个对象对应了一个4btye的引用。
3. 递归和迭代的区别
递归是通过一个函数进行的,函数使用了栈空间存放在函数中申明的基本数据类型(例如int,char)和堆中对象的引用(注意是引用,而非对象本身)。所以递归由于不断调用自身函数体,因此会导致需要使用的栈空间不断增加,最终导致栈溢出。
迭代是在一个函数中的循环体,迭代使用的pathstack这个变量,在JVM栈中只有一个4字节的指针(引用的本质),而变量的主体本身使用的则是堆内存空间。
堆空间相对于栈空间来说要大得多,但是如果无限制的使用堆空间,当然也会溢出,这就是java程序员都经常会遇到的OOM(OutOfMemoryError)异常。OOM异常是非常常见的堆溢出,而StackOverFlow这个栈溢出异常则非常少见,如文章所言,栈中只存放一些4字节的指针,所以虽然栈空间很小,但想撑爆栈空间也不是那么容易的事情,这就是为什么本地应用很难遇到栈溢出的原因。
递归就是在过程或函数里面调用自身,而迭代是利用变量的原值推算出变量的一个新值。如果递归是自己调用自己的话,迭代就是A不停的调用B。显然递归用了栈空间,迭代因为始终在一个函数体中,所以使用的是堆空间。迭代只会导致堆溢出(OOM)而不会导致栈溢出。
迭代不会使用栈空间,所以当然不会导致栈溢出。Stack类的实例变量因为使用的内存空间是处于堆空间中,因此当然可以避免栈溢出。