深层次树状结构遍历的改进 查找硬盘中的 “java.exe”

原创 2004年11月01日 19:37:00

原文:http://www.yi-dao.com/works/model/sample/findFiles.htm
遍历深层次的树状结构。这里的例子是查找所有本地硬盘中的“java.exe”文件。
当向一个深层的目录寻找特定的文件时,很容易想到通过不断地迭代来遍历。

import java.io.File;

public class FileSearchTool {
  public static void main(String[] args) {
    String toFind = "java.exe";
    File[] roots = File.listRoots();
    for (int i = 0; i < roots.length; i++) {
      searchList(roots[i], toFind);
    }
  }

  public static void searchList(File file, String toFind) {
    File[] files = file.listFiles();
    if (files != null) {
      for (int i = 0; i < files.length; i++) {
        if (files[i].isDirectory()) {
          searchList(file, toFind);
        }
        else if (files[i].getName().equals(toFind)) {
          System.out.println(files[i].getAbsolutePath());
        }
      }
    }
  }
}

上面的代码表面上看好象可以完成任务,可上面的方法却是无效的。上面忽略了目录的深度与广度,在递归的过程中不断地分配资源(File[] files = file.listFiles();),而只有当更深层的递归结束时才能结束当前的工作并释放这些资源,这样极容易导致堆栈溢出和 JVM 停止工作。
为了解决这样的问题,应该尽量避免使用递归。不用递归也不分配大量资源的方法才是有效的方法。

下面是不用递归时查找硬盘中的 "java.exe" 的程序。分析这个过程的状态机请见:http://www.yi-dao.com/works/model/sample/findFiles.ydm 下载这个文档,使用“易道模型”打开,易道模型下载:http://www.sharebank.com.cn/soft/soft_view.php?id=11135 
-------------------------------------------------------

public class Test{
  public static void main(String[] args) {
    File[] roots = File.listRoots();
    String nameToFind = "java.exe";
    for (int i = 0; i < roots.length; i++) {
      File[] files = findFiles(roots[i], nameToFind);
      for (int j = 0; j < files.length; j++) {
        System.out.println(files[j].getAbsolutePath());
      }
    }
  }

  public static File[] findFiles(File dir, String nameToFind) {
    Stack curPath = new Stack();
    curPath.push(dir);
    return findFiles(curPath, nameToFind);
  }

  public static final int FIND_SUB = 0; // 找子节点
  public static final int FIND_SIB = 1; // 找同级节点
  public static final int FIND_END = 2; // 结束
  public static File[] findFiles(Stack curPath, String nameToFind) {
    /** 只检测目录 */
    class MyDirFilter
        implements FileFilter {
      public boolean accept(File pathname) {
        return (pathname != null) && pathname.isDirectory();
      }
    }

    /** 只检测文件名相同 */
    class MyFileFilter
        implements FileFilter {
      String toFind;
      MyFileFilter(String toFind) {
        this.toFind = toFind;
      }

      public boolean accept(File pathname) {
        return (pathname != null) && pathname.isFile()
            && toFind.equalsIgnoreCase(pathname.getName());
      }
    }

    MyFileFilter fileFilter = new MyFileFilter(nameToFind);
    MyDirFilter dirFilter = new MyDirFilter();
    int state = FIND_SUB; // 开始
    LinkedHashSet found = new LinkedHashSet();
    while (state != FIND_END) {
      File dir = (File) curPath.pop(); // 当前目录
      // System.out.println(dir.getAbsolutePath());
      if (state == FIND_SUB) { // 查找子节点
        File[] subDirs = dir.listFiles(dirFilter);
        if (subDirs == null || subDirs.length == 0) { // 没有子节点
          curPath.push(dir);
          state = FIND_SIB; // 下一次需要找同级节点
        }
        else {
          curPath.push(dir);
          curPath.push(subDirs[0]);
          state = FIND_SUB;
        }
      }
      else if (state == FIND_SIB) { // 查找同级节点
        File[] files = dir.listFiles(fileFilter);
        if (files != null) {
          for (int i = 0; i < files.length; i++) {
            found.add(files[i]);
          }
        }

        if (curPath.isEmpty()) {
          state = FIND_END; // 已经没有可以找的了,需要退出查找过程
        }
        else {
          File parentDir = (File) curPath.peek();
          File[] sibDirs = parentDir.listFiles(dirFilter);
          for (int i = 0; i < sibDirs.length; i++) {
            if (dir.equals(sibDirs[i])) { // 找到了当前的位置
              if (i + 1 < sibDirs.length) { // 存在下一个同级节点
                curPath.push(sibDirs[i + 1]);
                state = FIND_SUB; // 需要查找子节点
              }
              else { // 这就是最后一个同级节点
                state = FIND_SIB;
              }
              break;
            }
          }
        }
      }
    }
    return (File[]) found.toArray(new File[found.size()]);
  }
}

上面的方法在整个文件目录的遍历过程中只使用了一个 LinkedHashSet 来记录当前的路径信息,并没有在向更深层次遍历时大量开销过多的资源,所以JVM是可以承受的。这样的方法才有效。

树状结构对象的获取及遍历

在程序开发过程中我们有时候需要在页面上实现树状结构数据的展现,比如地区树状这是很常见的需求。以下是我做的地区二级联动菜单,我是在页面初始化时从数据库中获取已经封装好的Area对象,这个对象包含我们需要...
  • swx18401204935
  • swx18401204935
  • 2017年01月06日 14:29
  • 274

计算机的硬盘中的事

计算机硬件性能在过去十年间的发展普遍遵循摩尔定律,通用计算机的CPU主频早已超过3GHz,内存也进入了普及DDR4的时代。然而传统硬盘虽然在存储容量上增长迅速,但是在读写性能上并无明显提升,同时SSD...
  • CDW2328
  • CDW2328
  • 2017年05月27日 09:14
  • 216

关于树状 结构 遍历根节点的所有子节点---验证可用

-查询指定节点及其所有子节点的函数 --表tb为我们设计的树状结构的表。pid为父id CREATE FUNCTION f_Cid(@ID char(3)) RETU...
  • lanchengxiaoxiao
  • lanchengxiaoxiao
  • 2012年05月15日 15:41
  • 465

oracle 递归 树形结构数据查询

connect by 是结构化查询中用到的,其基本语法是:  select ... from tablename start with cond1  connect by cond2  wher...
  • u011194983
  • u011194983
  • 2017年02月24日 18:06
  • 426

删除3721的方法 (转)

文章转载或出处〗≡中国电子技术信息网≡ 网址:www.CETINet.com 删除3721的方法 3721安装后,写了几个dll在 Winnt/Download program files下。并且在w...
  • benna
  • benna
  • 2005年06月29日 10:40
  • 913

如何用代码定位硬盘上的文件

问题:如何用代码控制资源浏览器,并定位到指定的文件?   答:使用ShellExecute,配合explorer即可 ShellExecute(Application.Handle, 'open...
  • shuaihj
  • shuaihj
  • 2011年10月13日 11:23
  • 2241

Java - 树状结构数据解析

由于工作中好多地方需要树状结构数据,在参考了网上递归实现和数据库存储过程实现,都不太满意。突然想到zTree也有解析该类数据的要求,所以在阅读了zTree源码的前提了,按照他的实现原理,写了一个jav...
  • yb642518034
  • yb642518034
  • 2016年10月15日 19:27
  • 1279

提取坏硬盘中的数据

一笔记本一开机不进系统,左上角光标闪烁,初步怀疑是系统故障,但是用U盘启动进PE检查时,无法找到硬盘,在BIOS中能检测到硬盘,怀疑硬盘MBR分区表被破坏,手上没有修复 工具,但是又要把数据取出来,...
  • jsship
  • jsship
  • 2011年07月31日 14:46
  • 2892

java.exe路径问题

因为要更换JDK版本,自然也就要重新设置JAVA_HOME环境变量,但设置完成后奇怪的发现,运行java -version时还是原来的版本,莫名其妙,最后我把JAVA_HOME环境变量删除竟然java...
  • xtayfjpk
  • xtayfjpk
  • 2013年12月02日 22:23
  • 1706

二分查找的改进--差值查找

差值查找 在二分查找中,我们每次比较都可以排除一半的数据量,这个已经是很高效了。如果利用关键字本身的信息,每次排除的数据量充分依赖于关键字的大小,则查找会更高效,这就是差值查找的思想。 下面通...
  • zhangxiangDavaid
  • zhangxiangDavaid
  • 2014年09月21日 00:31
  • 2844
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深层次树状结构遍历的改进 查找硬盘中的 “java.exe”
举报原因:
原因补充:

(最多只允许输入30个字)