代码之家  ›  专栏  ›  技术社区  ›  dierre

不使用递归遍历目录?

  •  5
  • dierre  · 技术社区  · 14 年前

    问题 我需要编写一个简单的软件,给出一些约束条件,将一系列文件附加到列表中。 用户可以在两种“类型”的目录中进行选择:一种带有 * 通配符意味着它还应该探索子目录和不带通配符的经典子目录,这些子目录只获取该目录中的文件。

    我在做什么

    现在我在做最愚蠢的事:

    import java.io.File;
    
    public class Eseguibile {
    
        private static void displayIt(File node){
    
            System.out.println(node.getAbsoluteFile());
    
            if(node.isDirectory()){
                String[] subNote = node.list();
                for(String filename : subNote){
                    displayIt(new File(node, filename));
                }
            }
        }
    
        public static void main(String[] args){
            System.out.println("ciao");
    
            displayIt( new File("/home/dierre/") );
    
        }
    
    }
    

    我不需要建立一棵树,因为我只需要文件列表,所以我想也许有一个更有效的方法来做。

    我在读关于 TreeModel 但是,据我所知,它只是一个实现Jtree的接口。

    8 回复  |  直到 14 年前
        1
  •  8
  •   Stephen C    14 年前

    现在我在做最愚蠢的事。。。

    递归既不“愚蠢”,也不一定低效。实际上,在这种特殊情况下,递归解决方案可能比非递归解决方案更有效。当然,递归解决方案比其他方案更易于编写代码和理解。

    递归的唯一潜在问题是,如果目录树在病理上很深,则可能会溢出堆栈。

    如果您真的想避免递归,那么自然的方法是使用“文件列表堆栈”数据结构。在每个递归的地方,将包含当前目录(剩余)文件对象的列表推送到堆栈上,读取新目录并开始处理它们。完成后,弹出堆栈并继续父目录。这将给你一个深度优先遍历。如果需要宽度优先遍历,请使用“文件队列”数据结构而不是堆栈。

        2
  •  2
  •   Lucas Batistussi    11 年前

      ArrayDeque<File> stack = new ArrayDeque<File>();
    
        stack.push(new File("<path>"));
    
        int n = 0;
    
        while(!stack.isEmpty()){
    
            n++;
            File file = stack.pop();
    
            System.err.println(file);
    
            File[] files = file.listFiles();
    
            for(File f: files){
    
                if(f.isHidden()) continue;
    
                if(f.isDirectory()){
                    stack.push(f);
                    continue;
                }
    
                n++;
                System.out.println(f);
    
    
            }
    
        }
    
        System.out.println(n);
    
        3
  •  1
  •   PATRY Guillaume    14 年前

    递归始终可以转换为循环。
    一个快速且肮脏的可能解决方案(未测试)如下:

    private static void displayDirectory(File node){
        ArraList directories = new ArrayList();
        if (node.isDirectory())
           directories.append (node);    
        // Iterate over the directory list
        Iterator it = directories.iterator();
        while(it.hasNext()){
           File dir  = (File)it.next();           
           // get childs
           String[] subNote = dir.list();
           for(String filename : subNote){
              subNode = new File(node, filename);
              // display current child name
              System.out.println(subNode.getAbsoluteFile());
              // if directory : add current child to the list of dir to process
              if (subnode.isDirectory()){
                 directories.append(subNode);
              }
           }
        }
    }
    

    请注意,源节点应该是显示任何内容的目录。
    此外,这是一个广度优先的展示。如果首先需要深度,应该更改“append”,将文件放在数组列表中当前节点之后。

    不过,我不确定记忆的组合。
    当做
    纪尧姆

        4
  •  1
  •   dierre    13 年前

    我是一个真正的新手,但在这个问题上工作了一周之后。。。我有一个干净的解决方案。。。谢谢帕特里和埃巴尔的帮助。

    public class Recur {
        // Process only directories under dir
    
    File dir;
    static DirectoryComponent allDirectory;
    
        public Recur(File dir, DirectoryComponent allDirectory) {
        // TODO Auto-generated constructor stub
            this.dir = dir;
    }
    
        public static DirectoryComponent Recur(File dir, DirectoryComponent allDirectory) {
            String file;
            String path;
    
             File firstDir = new File(dir.getPath());
             file = firstDir.getName();
             path = firstDir.getPath();
    
            if (dir.isDirectory()) {
    
                file = firstDir.getName();
                path = firstDir.getPath();
                DirectoryComponent directory = new Directory(file, path);
                allDirectory.add(directory);
    
                String [] subNote = dir.list();
                for(String filename : subNote){
                    File subNode = new File(firstDir, filename);
    
                    // store current child name
    
                        file = subNode.getName();
                        path = subNode.getPath();
                        directory.add(new FileItem(file,path));         
                }
    
                String[] children = dir.list();
                for (int i=0; i<children.length; i++) {
                    Recur(new File(dir, children[i]), allDirectory);
                }
            }
    
            return allDirectory;
        }
    }
    
        5
  •  1
  •   Stefan Steiger    8 年前

    根据你的要求,下面是我在评论中提到的基准代码(C#-不是Java)。
    请注意,它应该使用秒表,而不是日期时间,以获得更好的准确性,但否则它是好的。
    我没有测试迭代是否提供与递归相同的数字文件,但它应该。

    实际上,如果你注意中间值,你会注意到这已经开始显示只有很少的文件。 (我的桌面文件夹包含2210个文件,415个文件夹,总共3.2 GB,其中大部分是下载文件夹中的大文件,AppData,以及更多的文件,因为我的桌面上有一个较大的C#项目[邮件服务器])。

    Speed comparison

    正如评论中提到的,说这无关紧要并不完全正确。

    树不需要在病态上很深就可以注意到这种效果(虽然慢速度不是堆栈溢出,但其非常负面的结果与stack overflow Bug没有太大区别)。我也不会说有很多文件是“病态的”,因为如果你在主驱动器上做一个索引,你自然会有很多文件。有一些HTML文档,文件的数量就会爆炸。你会发现,在很多文件中,迭代在30秒内完成,而递归需要appx。3分钟。

    using System;
    using System.Data;
    using System.Linq;
    
    
    namespace IterativeDirectoryCSharp
    {
    
    
        public class SearchStrategy
        {
    
    
            //Iterative File and Folder Listing in VB.NET
            public static bool IterativeSearch2(string strPath)
            {
                System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(strPath);
                System.IO.FileSystemInfo[] arrfsiEntities = null;
                arrfsiEntities = dirInfo.GetFileSystemInfos();
    
    
                // Creates and initializes a new Stack.
                System.Collections.Stack strLastPathStack = new System.Collections.Stack();
                System.Collections.Stack iIndexStack = new System.Collections.Stack();
    
                int iIndex = 0;
                int iMaxEntities = arrfsiEntities.Length;
                do
                {
                    while (iIndex < iMaxEntities)
                    {
    
                        if (arrfsiEntities[iIndex].Attributes == System.IO.FileAttributes.Directory)
                        {
                            //Console.WriteLine("Searching directory " + arrfsiEntities[iIndex].FullName);
    
                            strLastPathStack.Push(System.IO.Directory.GetParent(arrfsiEntities[iIndex].FullName).FullName);
                            strLastPathStack.Push(arrfsiEntities[iIndex].FullName);
                            iIndexStack.Push(iIndex);
    
                            dirInfo = null;
                            Array.Clear(arrfsiEntities, 0, arrfsiEntities.Length);
                            dirInfo = new System.IO.DirectoryInfo(strLastPathStack.Pop().ToString());
                            arrfsiEntities = dirInfo.GetFileSystemInfos();
    
                            iIndex = 0;
                            iMaxEntities = arrfsiEntities.Length;
                            continue;
                        }
                        else
                        {
                            //Console.WriteLine(arrfsiEntities[iIndex].FullName);
                        }
    
                        iIndex += 1;
                    } // Whend
    
    
                    dirInfo = null;
                    Array.Clear(arrfsiEntities, 0, arrfsiEntities.Length);
                    // Dont try to do move the next line in the loop while/until statement, null reference when pop on an empty stack...
                    if (strLastPathStack.Count == 0) 
                        break;
    
                    dirInfo = new System.IO.DirectoryInfo(strLastPathStack.Pop().ToString());
                    arrfsiEntities = dirInfo.GetFileSystemInfos();
    
                    iIndex = (int)iIndexStack.Pop() + 1;
                    iMaxEntities = arrfsiEntities.Length;
                } // End do
                while (true);
    
                return true;
            } // End Function IterativeSearch2
    
    
            public static bool IterativeSearch1(string path)
            {
    
                System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(path);
                System.IO.FileSystemInfo[] arrfsiEntities = null;
                arrfsiEntities = dirInfo.GetFileSystemInfos();
    
    
                // Creates and initializes a new Stack.
                System.Collections.Stack myStack = new System.Collections.Stack();
                //Console.WriteLine("Stack is empty when \t stack.count={0}", myStack.Count);
    
    
                int iIndex = 0;
                int iMaxEntities = arrfsiEntities.Length - 1;
    
                do
                {
                    for (iIndex = 0; iIndex <= iMaxEntities; iIndex += 1)
                    {
                        if (arrfsiEntities[iIndex].Attributes == System.IO.FileAttributes.Directory)
                        {
                            //Console.WriteLine("Searching directory " + arrfsiEntities[iIndex].FullName);
                            myStack.Push(arrfsiEntities[iIndex].FullName);
                        }
                        else
                        {
                            //Console.WriteLine("{0}", arrfsiEntities[iIndex].FullName);
                        }
                    } // Next iIndex
    
                    dirInfo = null;
                    Array.Clear(arrfsiEntities, 0, arrfsiEntities.Length);
                    // Dont try to do move the next line in the loop while/until statement, null reference when pop on an empty stack...
                    if (myStack.Count == 0) 
                        break;
    
                    dirInfo = new System.IO.DirectoryInfo(myStack.Pop().ToString());
                    arrfsiEntities = dirInfo.GetFileSystemInfos();
    
                    iIndex = 0;
                    iMaxEntities = arrfsiEntities.Length - 1;
                }
                while (true);
    
                return true;
            } // End Function IterativeSearch1
    
    
            //Recursive File and Folder Listing VB.NET
            public static bool RecursiveSearch(string path)
            {
                System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(path);
                //System.IO.FileSystemInfo fileObject = default(System.IO.FileSystemInfo);
                foreach (System.IO.FileSystemInfo fsiThisEntityInfo in dirInfo.GetFileSystemInfos())
                {
    
                    if (fsiThisEntityInfo.Attributes == System.IO.FileAttributes.Directory)
                    {
                        //Console.WriteLine("Searching directory " + fsiThisEntityInfo.FullName);
                        RecursiveSearch(fsiThisEntityInfo.FullName);
                    }
                    else
                    {
                        //Console.WriteLine(fsiThisEntityInfo.FullName);
                    }
    
                } // Next fiThisFileInfo
    
                return true;
            } // End Function RecursiveSearch
    
    
            // http://forums.asp.net/p/1414929/3116196.aspx
            public class TimeFrame
            {
                public DateTime dtStartTime;
                public DateTime dtEndTime;
    
                public TimeFrame(DateTime dtStart, DateTime dtEnd)
                {
                    this.dtStartTime = dtStart;
                    this.dtEndTime = dtEnd;
                } // End Constructor
    
            } // End Class TimeFrame
    
    
    
            // Small amount of files 
            //          Iter1       Iter2       Recurs.
            // Median   1206.231    3910.367    1232.4079
            // Average  1216.431647 3940.147975 1239.092354
            // Minimum  1172.5827   3832.0984   1201.2308
            // Maximum  1393.4091   4400.4237   1440.3386
    
            public static System.Data.DataTable TestStrategies(string strDirectoryToSearch)
            {
                System.Data.DataTable dt = new System.Data.DataTable();
    
                System.Collections.Generic.Dictionary<int, string> dictResults = new System.Collections.Generic.Dictionary<int, string>();
    
    
                dt.Columns.Add("TestRun", typeof(string));
                dt.Columns.Add("IterativeSearch1", typeof(double));
                dt.Columns.Add("IterativeSearch2", typeof(double));
                dt.Columns.Add("RecursiveSearch", typeof(double));
    
    
                System.Data.DataRow dr = null;
                System.Collections.Generic.Dictionary<string, TimeFrame> dictPerformance = null;
                for (int i = 0; i < 100; ++i)
                {
                    dr = dt.NewRow();
                    dr["TestRun"] = i + 1;
    
                    dictPerformance = new System.Collections.Generic.Dictionary<string, TimeFrame>();
                    DateTime startTime;
                    DateTime endTime;
                    Console.WriteLine("*********************************************************");
    
                    startTime = DateTime.Now;
                    IterativeSearch1(strDirectoryToSearch);
                    endTime = DateTime.Now;
                    dictPerformance.Add("IterativeSearch1", new TimeFrame(startTime, endTime));
    
                    Console.WriteLine("*********************************************************");
    
                    startTime = DateTime.Now;
                    IterativeSearch2(strDirectoryToSearch);
                    endTime = DateTime.Now;
                    dictPerformance.Add("IterativeSearch2", new TimeFrame(startTime, endTime));
    
                    Console.WriteLine("*********************************************************");
    
                    startTime = DateTime.Now;
                    RecursiveSearch(strDirectoryToSearch);
                    endTime = DateTime.Now;
                    dictPerformance.Add("RecursiveSearch", new TimeFrame(startTime, endTime));
    
                    Console.WriteLine("*********************************************************");
    
                    string strResult = "";
                    foreach (string strKey in dictPerformance.Keys)
                    {
                        TimeSpan elapsedTime = dictPerformance[strKey].dtEndTime - dictPerformance[strKey].dtStartTime;
    
                        dr[strKey] = elapsedTime.TotalMilliseconds;
                        strResult += strKey + ": " + elapsedTime.TotalMilliseconds.ToString() + Environment.NewLine;
                    } // Next
    
                    //Console.WriteLine(strResult);    
                    dictResults.Add(i, strResult);
                    dt.Rows.Add(dr);
                } // Next i
    
    
                foreach(int iMeasurement in dictResults.Keys)
                {
                    Console.WriteLine("Measurement " + iMeasurement.ToString());
                    Console.WriteLine(dictResults[iMeasurement]);
                    Console.WriteLine(Environment.NewLine);
                } // Next iMeasurement
    
    
                double[] adblIterSearch1 = dt
                     .AsEnumerable()
                     .Select(row => row.Field<double>("IterativeSearch1"))
                     .ToArray();
    
                double[] adblIterSearch2 = dt
                     .AsEnumerable()
                     .Select(row => row.Field<double>("IterativeSearch2"))
                     .ToArray();
    
                double[] adblRecursiveSearch = dt
                     .AsEnumerable()
                     .Select(row => row.Field<double>("RecursiveSearch"))
                     .ToArray(); 
    
    
    
                dr = dt.NewRow();
                dr["TestRun"] = "Median";
                dr["IterativeSearch1"] = Median<double>(adblIterSearch1);
                dr["IterativeSearch2"] = Median<double>(adblIterSearch2);
                dr["RecursiveSearch"] = Median<double>(adblRecursiveSearch);
                dt.Rows.Add(dr);
    
    
                dr = dt.NewRow();
                dr["TestRun"] = "Average";
                dr["IterativeSearch1"] = dt.Compute("Avg(IterativeSearch1)", string.Empty);
                dr["IterativeSearch2"] = dt.Compute("Avg(IterativeSearch2)", string.Empty);
                dr["RecursiveSearch"] = dt.Compute("Avg(RecursiveSearch)", string.Empty);
                dt.Rows.Add(dr);
    
    
                dr = dt.NewRow();
                dr["TestRun"] = "Minimum ";
                dr["IterativeSearch1"] = dt.Compute("Min(IterativeSearch1)", string.Empty);
                dr["IterativeSearch2"] = dt.Compute("Min(IterativeSearch2)", string.Empty);
                dr["RecursiveSearch"] = dt.Compute("Min(RecursiveSearch)", string.Empty);
                dt.Rows.Add(dr);
    
                dr = dt.NewRow();
                dr["TestRun"] = "Maximum ";
                dr["IterativeSearch1"] = dt.Compute("Max(IterativeSearch1)", string.Empty);
                dr["IterativeSearch2"] = dt.Compute("Max(IterativeSearch2)", string.Empty);
                dr["RecursiveSearch"] = dt.Compute("Max(RecursiveSearch)", string.Empty);
                dt.Rows.Add(dr);
    
                return dt;
            } // End Sub TestMain
    
    
            public static double Median<T>(T[] numbers)
            {
                int numberCount = numbers.Count();
    
                if (numberCount == 0)
                    return 0.0;
    
                int halfIndex = numbers.Count() / 2;
                var sortedNumbers = numbers.OrderBy(n => n);
                double median;
    
                if ((numberCount % 2) == 0)
                {
                    median = 
                        (
                            (
                                System.Convert.ToDouble(sortedNumbers.ElementAt<T>(halfIndex)) +
                                System.Convert.ToDouble(sortedNumbers.ElementAt<T>((halfIndex - 1))
                            )
                        ) / 2);
                }
                else
                {
                    median = System.Convert.ToDouble(sortedNumbers.ElementAt<T>(halfIndex));
                }
    
                return median;
            } // End Function GetMedian
    
    
            // http://msmvps.com/blogs/deborahk/archive/2010/05/07/linq-mean-median-and-mode.aspx
            public static double CalcMedian(int[] numbers)
            {
                int numberCount = numbers.Count();
                int halfIndex = numbers.Count() / 2;
                var sortedNumbers = numbers.OrderBy(n => n);
                double median;
    
                if ((numberCount % 2) == 0)
                {
                    median = ((sortedNumbers.ElementAt(halfIndex) +
                        sortedNumbers.ElementAt((halfIndex - 1))) / 2);
                }
                else
                {
                    median = sortedNumbers.ElementAt(halfIndex);
                }
    
                return median;
            } // End Function CalcMedian
    
    
        } // End Class SearchStrategy
    
    
    } // End Namespace IterativeDirectoryCSharp
    
        6
  •  0
  •   Etienne Marais    14 年前

    如果你选择使用递归,我发现一个例子,可能是接近你目前使用的,以消除任何歧义。

    // Process only directories under dir
    public static void visitAllDirs(File dir) {
        if (dir.isDirectory()) {
            process(dir);
    
            String[] children = dir.list();
            for (int i=0; i<children.length; i++) {
                visitAllDirs(new File(dir, children[i]));
            }
        }
    }
    

    这是一个非常简单的例子 process()

        7
  •  0
  •   Fahim Parkar    12 年前

    派对,谢谢你的建议。我把你的代码改了一点

    private ArrayList<File> displayDirectory(File node){
    ArrayList<File> FileList = new ArrayList();
    ArrayList <File>directories = new <File>ArrayList();
    if (node.isDirectory())
       directories.add(node);
    // Iterate over the directory list
    Iterator it = directories.iterator();
    for (int i = 0 ; i < directories.size();i++){
       File dir  =  directories.get(i);
       // get childs
       String[] subNode = dir.list();
       for(int j = 0 ; j < subNode.length;j++){
          File F = new File( directories.get(i).getAbsolutePath(), subNode[j]);
          // display current child name
        //  System.out.println(F.getAbsoluteFile());
          // if directory : add current child to the list of dir to process
          if (F.isDirectory()) directories.add(F);           
            else   FileList.add(F);
          }
    }   
    return FileList;
    }
    
        8
  •  0
  •   anemomylos    7 年前

    基于PATRY-Guillaume的解决方案

    public static List<File> getFolderTree(File node)
      {
        List<File> directories = new ArrayList();
        if (node.isDirectory())
        {
          directories.add(node);
        }
    
        for(int i=0; i<directories.size(); i++)
        {
          File dir = directories.get(i);
          String[] subNote = dir.list();
          for (String filename : subNote)
          {
            File subNode = new File(dir, filename);
    
            if (subNode.isDirectory())
            {
              directories.add(subNode);
            }
          }
        }
        return directories;
      }