遞歸方法
import java.io.File;
import java.util.*;
public class test3 {
public static List<File> recurseDirs(String start,String regex) {
return recurseDirs(new File(start), regex);
}
public static List<File> recurseDirs(String start) {
return recurseDirs(new File(start), ".*");
}
public static List<File> recurseDirs(File startDir) {
return recurseDirs(startDir, ".*");
}
public static List<File> recurseDirs(File startDir, String regex) {
List<File> result = new ArrayList<File>();
if (startDir.listFiles() == null) {//防止主函數調用時給出路徑不是一個有效的目錄
System.out.println("這不是一個有效的目錄");
return result;//直接返回空的list
}
for(File item : startDir.listFiles()) {
if(item.isDirectory()) {
result.add(item);
result.addAll(recurseDirs(item));
} else {
if(item.getName().matches(regex))
result.add(item);
}
}
return result;
}
public static void main(String[] args) {
List<File> files = recurseDirs("E:/影視作品");
for(File i:files) {
System.out.println(i);
}
}
}
其實這個例子來自JAVA編程思想——18.1.2目錄實用工具——Directory類。看了書中代碼着實覺得這種遞歸實現太優雅了,所以我改了改變成了自己的實現。
public static List<File> recurseDirs(File startDir, String regex)
這個簽名的方法才是主要的函數,其他的都是重載版本,regex參數就是你要設置的正則表達式,如果你設置了regex形參,那么只有符合該regex的File才會返回。- 關於遞歸設計,書中用的是一個作者自己寫的一個TreeInfo靜態內部類,它實現了一個addAll方法,但實際上它內部也用到了List接口的addAll,所以我這里就改成了使用ArrayList來存儲File對象。
這個遞歸的精妙之處:
- 首先多虧了List接口里自帶addAll這個接口,要是沒有這個addAll,那么result用
File[]
作為類型也一樣(recurseDirs的返回值也改成File[]
),然后單獨寫一個靜態方法,可以是這樣的簽名public static File[] addAll(File[] a, File[] b)
,然后這句result.addAll(recurseDirs(item));
改成result = addAll(result, recurseDirs(item))
。 - 只要是調用了遞歸方法體,傳進來的startDir必定是一個目錄(isDirectory返回true),這是由循環中調用遞歸的前提判斷來保證的。
- 在循環中,如果發現循環變量item是一個當前目錄下的文件,那么執行else分支,不會調用到遞歸函數;如果發現循環變量item是一個當前目錄下的子目錄,那么執行if分支,會調用到遞歸函數,調用前先添加這個子目錄。
其他:
- listFiles返回一個
File[]
,如果調用的File不是一個有效的目錄,返回null。 if (startDir.listFiles() == null)
分支只是為了防止主函數調用時給出路徑不是一個有效的目錄,如果不是有效目錄且沒有這個判斷,下面的循環會拋出空指針異常。只要主函數給的有效目錄,之后就不會出錯了。
遞歸+限制目錄深度
import java.io.File;
import java.util.*;
public class test4 {
public static List<File> recurseDirs(File startDir, int count) {
List<File> result = new ArrayList<File>();
if (count == 0) {
return result;//直接返回空的list
}
if (startDir.listFiles() == null) {//防止主函數調用時給出路徑不是一個有效的目錄
System.out.println("這不是一個有效的目錄");
return result;//直接返回空的list
}
for(File item : startDir.listFiles()) {
if(item.isDirectory()) {
result.add(item);
result.addAll(recurseDirs(item, count - 1));
} else {
result.add(item);
}
}
return result;
}
public static void main(String[] args) {
List<File> files = recurseDirs(new File("E:/影視作品"),2);
for(File i:files) {
System.out.println(i);
}
}
}
給recurseDirs加一個參數代表遞歸目錄的深度,為2時代表深度為2。主函數中,給的目錄下的所有文件或子目錄算是1的深度。
主函數中,給的目錄是E:/影視作品
,打印結果如下:
可見深度只能到2。
BFS
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class test5 {
public static List<File> BFS(File startDir) {
List<File> result = new ArrayList<File>();
if (startDir.listFiles() == null) {//防止主函數調用時給出路徑不是一個有效的目錄
System.out.println("這不是一個有效的目錄");
return result;//直接返回空的list
}
LinkedList<File> queue = new LinkedList<>();//用作隊列
queue.addAll(Arrays.asList(startDir.listFiles()));//給隊列起個頭
while (!queue.isEmpty()) {//只要隊列不為空
//取出隊列的第一個元素
File item = queue.removeFirst();
if (item.isDirectory())//如果當前元素是目錄
queue.addAll(Arrays.asList(item.listFiles()));//往隊列里加入目錄下的所有文件
result.add(item);//總是加入當前元素
}
return result;
}
public static void main(String[] args) {
List<File> files = BFS(new File("E:/影視作品"));
for(File i:files) {
System.out.println(i);
}
}
}
遞歸是深度遍歷,既然深度遍歷用了,那再試試寬度遍歷唄。使用LinkedList用作寬度遍歷中的隊列。