java 不使用递归遍历目录?

gcxthw6b  于 2022-12-10  发布在  Java
关注(0)|答案(8)|浏览(118)

***问题***我需要编写一个简单的软件,在给定一定的约束条件下,将一系列文件附加到一个列表中。用户可以在两种“类型”的目录中进行选择:一种是带有***通配符的,表示它还应该浏览子目录;另一种是不带通配符的经典方法,它只获取该目录中存在的文件。

"我在做什么"
现在我在做一件最愚蠢的事

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的接口。

qyswt5oh

qyswt5oh1#

现在我正在做一件最愚蠢的事...
递归既不“愚蠢”,也不一定是低效的。实际上,在这种特殊情况下,递归解决方案可能比非递归解决方案更有效。当然,递归解决方案比其他解决方案更容易编码和理解。
递归的唯一潜在问题是,如果目录树太深,堆栈可能会溢出。
如果你真的想避免递归,那么最自然的方法就是使用“文件列表堆栈”数据结构。(剩余)将对象归档到堆栈中,读取新目录并开始处理它们。完成后,弹出堆栈并继续父目录。这将给予你一个深度优先遍历。如果你想要一个广度优先遍历,使用“文件队列”数据结构而不是堆栈。

o2gm4chl

o2gm4chl2#

我的迭代解决方案:

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

ippsafx73#

如果有帮助的话,这里有一个C#版本的迭代文件系统遍历:

public static System.Collections.Generic.List<System.IO.FileSystemInfo>
    ListPaths(System.IO.FileSystemInfo path)
{
    System.Collections.Generic.List<System.IO.FileSystemInfo> ls =
        new System.Collections.Generic.List<System.IO.FileSystemInfo>();

    System.Collections.Generic.Stack<System.IO.FileSystemInfo> stack
        = new System.Collections.Generic.Stack<System.IO.FileSystemInfo>();

    stack.Push(path);

    int n = 0;

    while (stack.Count != 0)
    {
        ++n; // Every element from the stack counts 
        System.IO.FileSystemInfo file = stack.Pop();
        // System.Console.WriteLine(file);

        ls.Add(file); // These are all directories, unless the first element is a file 

        if (file is System.IO.DirectoryInfo == false)
            continue;

        foreach (System.IO.FileSystemInfo entry in ((System.IO.DirectoryInfo)file).GetFileSystemInfos())
        {
            if (entry.Attributes.HasFlag(System.IO.FileAttributes.Hidden))
                continue;

            if (entry is System.IO.DirectoryInfo)
            {
                stack.Push(entry);
                continue;
            }

            ++n; 
            // System.Console.WriteLine(entry);
            ls.Add(entry); // These are all files 
        } // Next entry 

    } // Whend 

    // n = ls.Count
    return ls;
} // End Function ListPaths

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

实际上,如果您注意一下中间值,您会注意到这已经开始显示只有很少的文件。(我的桌面文件夹包含2210个文件,415个文件夹,总共3.2 GB,其中大部分是下载文件夹AppData中的大文件,由于我的桌面上有一个较大的C#项目[mail-server],因此文件数量更多)。

要得到我在评论中提到的数字,安装cygwin(所有的东西[我想大约是100 GB]),并索引cygwin文件夹。

正如评论中提到的,说没关系并不完全正确。
而对于一个小的目录树,递归比迭代效率高得多,可以忽略不计(以几十毫秒的数量级),对于一个非常大的树,递归是分钟如果你必须分配并返回一组新的堆栈变量,那么每次调用一个函数,并存储所有先前的结果,直到你返回为止,当然,这要比在堆上初始化一个堆栈结构并在每次迭代中使用它慢。
树并不需要病理学上的深度来注意这种效果(虽然速度慢不是堆栈溢出,但它的负面后果与StackOverflow-Bug没有太大区别).我也不会把拥有大量文件称为“病态,”因为如果你在主驱动器上创建索引,你自然会拥有大量文件.有一些HTML文档,并且文件的数量爆炸式增长,你会发现对于大量的文件,一次迭代不到30秒就完成了,而一次递归需要大约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

如果需要保留遍历顺序,简化版本如下:

public static void PathTraverse(string initialDirectory)
{
    System.Collections.Generic.Stack<string> stack =
        new System.Collections.Generic.Stack<string>();
    stack.Push(initialDirectory);

    while (stack.Count != 0)
    {
        string element = stack.Pop();
        System.Console.WriteLine(element);

        System.IO.FileAttributes attr = System.IO.File.GetAttributes(element);
        if ((attr & System.IO.FileAttributes.Directory) == System.IO.FileAttributes.Directory)
        {
            string[] children = System.IO.Directory.GetFileSystemEntries(element, "*.*", System.IO.SearchOption.TopDirectoryOnly);

            for (int i = children.Length - 1; i > -1; --i)
            {
                stack.Push(children[i]);
            }
        }
    }

} // End Function PathTraverse
ct3nt3jp

ct3nt3jp4#

递归总是可以转换成循环。
一个快速且不明确的可能解决方案(未测试)如下:

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”,把文件放在数组列表中当前节点的后面。
不过,我不确定记忆是否会消失。
此致
纪尧姆

r7s23pms

r7s23pms5#

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

// 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()可以是你对目录进行处理或操作的地方。

ryevplcw

ryevplcw6#

我是一个真实的的新手,但是在这个问题上工作了一个星期之后...我有了一个干净的解决方案...感谢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;
    }
}
zxlwwiss

zxlwwiss7#

PARTY,谢谢你的建议。我对你的代码做了一点修改,这就是我所得到的

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

0qx6xfy68#

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

相关问题