來自: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類的實例變量因為使用的內存空間是處於堆空間中,因此當然可以避免棧溢出。