遍历目录而不使用递归?

时间:2010-10-14 10:55:16

标签: java recursion

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

我在做什么

现在我正在做最愚蠢的事情:

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 个答案:

答案 0 :(得分:8)

  

现在我正在做最愚蠢的事情......

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

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

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

答案 1 :(得分:1)

递归总是可以转换成循环 下面是一个快速而肮脏的解决方案(未经测试):

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”将文件放在数组列表中的当前节点之后。

但是,我不确定记忆的安慰 问候
纪尧姆

答案 2 :(得分:1)

我是一个真正的新手,但在这个问题上工作了一个星期......我有一个干净的解决方案......感谢PATRY和etbal的所有帮助。

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;
    }
}

答案 3 :(得分:1)

我的迭代解决方案:

  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);

答案 4 :(得分:1)

@Stephen C: 根据您的要求,我在评论中谈到了我的基准代码(C# - 不是Java)。
请注意,它应该使用秒表代替日期时间以获得更好的准确性,但除此之外它没有问题 我没有测试迭代是否提供与递归相同的数字文件,但它应该。

实际上,如果你注意中位数,你会发现它已经开始只显示很少的文件。(我的桌面文件夹包含2210个文件,415个文件夹,3.2 GB总的来说,下载文件夹中的大部分文件,AppData,以及由于桌面上的一个较大的C#项目[mail-server]而导致的文件数量较多。

要获得我在评论中谈到的数字,请安装cygwin(包含所有内容[我认为大约100GB]),然后索引cygwin文件夹。

Speed comparison

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

而对于小目录树,递归比迭代(几十毫秒的数量级)的效率可忽略不计,对于非常大的树,递归比迭代慢几分钟(因此明显)慢。要弄清楚原因也不难。如果你必须分配并返回一组新的堆栈变量,每次调用一个函数,并存储所有先前的结果,直到你返回,你当然比在堆上启动一个堆栈结构时慢一些,并为每次迭代使用它。

树不需要在病理学上深入注意这种效果(虽然慢速不是堆栈溢出,但其非常不利的后果与StackOverflow-Bug没有太大区别)。此外,我不会打电话给很多文件&#34;病态&#34;,因为如果你在主驱动器上做索引,你自然会有很多文件。有一些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

答案 5 :(得分:0)

如果您选择使用递归,我找到了一个可能与您当前使用的示例相近的示例,以消除任何歧义。

// 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()可以是您在目录上处理或操作的地方。

答案 6 :(得分:0)

派对,谢谢你的建议。我改变了你的代码,这就是我所拥有的

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;
}

答案 7 :(得分:0)

基于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;
  }