工作总结|文件系统模型的三种实现

最近在做关于压缩文件预览的需求,涉及到对于文件系统的目录结构进行简单的模拟。在实现过程中,尝试了三种实现方式,下文对三种实现方式做详细说明。

传统目录结构

使用递归构建文件二叉树结构

  • 最初的思路是:每次从输入流中读入一个结点,判断根据结点的路径名判断,如果是其子节点,则放入结点的右子树。如果是其同级结点,则放入其左子树中。(与上学时学的数据结构中多叉树转二叉树的区别在于:书上实现要求左孩子,右兄弟,我的实现是左兄弟,右孩子)
  • 类模型代码
public class CompressedFileInfo {
    public String filePath;
    public String parentPath;
    private String name;
    public boolean isDirectory;
    private int levels;
    private CompressedFileInfo leftFriendInfo;
    private CompressedFileInfo rightChildInfo;
}
  • 递归创建二叉树结构代码
  public void buildFileInfo(CompressedFileInfo root) {
          if (root == null) {
              return;
          }
          if (root.levels == 0) {
              if (root.rightChildInfo == null) {
                  root.rightChildInfo = this;
                  return;
              } else {
                  root = root.rightChildInfo;
              }
          }
          if (root.levels < levels) {
              if (filePath.startsWith(root.filePath)) {
                  if (root.rightChildInfo == null) {
                      root.rightChildInfo = this;
                  } else {
                      buildFileInfo(root.rightChildInfo);
                  }
              } else {
                  buildFileInfo(root.leftFriendInfo);
              }
          } else if (root.levels == levels) {
              if (parentPath == null || filePath.startsWith(root.filePath)) {
                  if (root.leftFriendInfo == null) {
                      root.leftFriendInfo = this;
                  } else {
                      buildFileInfo(root.leftFriendInfo);
                  }
              } else {
                  Log.d("raytest", "Build Error");
              }
          } else {
              Log.e("raytest", "Build Error");
          }
  }
  • 采用二叉树构建的文件结构图如下所示:
二叉树目录结构
  • 代码总结与分析
    在构建中,根据节点初始化的levels来对结点的层级数进行记录,在递归过程中,注意要结合父一级路径进行遍历。否则,可能会出现结点组织错误的问题。
    二叉树作为一种存储方式是一种很优雅的选择。但是对于我们项目中的需求来说,此种构建方式严重依赖于外部结点输入,必须保证从根节点,深度优先遍历,提供输入结点信息。否则并不能保证结点组织的准确性。同时二叉树递归遍历,在实际项目使用中,代码阅读性差,使用效率不高。所以,考虑到这些问题,我们决定采用多叉树型结构来实现文件结构

使用递归构建文件树形结构

  • 采用树形结构存储,为了摆脱对外部结点输入的依赖,引入一个缓存Map来缓存所有输入结点,在结点输入完毕后,通过递归,构建树形结构,在摆脱外部输入的基础上,提高了程序的可读性。

  • 类模型代码

public class DecompressFileItem extends FileItem {
    private String mParentPath;
    private List<FileItem> mChildList;
}

此处的FileItem可以理解为File对象属性的一些映射,包含一些获取文件名,获取文件路径之类的常用属性和方法。

  • 递归创建
public void buildFileSys(Map<String, FileItem> nodeMap, DecompressFileItem paramInfo, boolean isRoot) {
        Map<String, FileItem> tempMap = new HashMap<>();
        tempMap.putAll(nodeMap);
        if (paramInfo == null || TextUtils.isEmpty(paramInfo.mData)) {
            return;
        }
        if (nodeMap == null || nodeMap.size() < 1) {
            return;
        }
        String paramPathKey = paramInfo.mData;
        if (isRoot) {
            paramPathKey = paramPathKey.substring(0, paramPathKey.lastIndexOf(File.separator));
            tempMap.remove(paramPathKey);
        } else {
            tempMap.remove(paramInfo.mData);
        }
        for (Map.Entry<String, FileItem> entry : tempMap.entrySet()) {
            DecompressFileItem item = (DecompressFileItem) entry.getValue();
            if (paramPathKey.equals(item.getParentPath())) {
                if (paramInfo.mChildList == null) {
                    paramInfo.mChildList = new ArrayList<>();
                }
                paramInfo.mChildList.add(item);
                item.buildFileSys(tempMap, item, false);
            }
        }
    }
  • 代码总结与分析
    在构建中,每次都将所有结点放置于一个暂存map中,将当前结点移除map中,并遍历剩余结点,将其子节点加入list中,递归遍历子节点,自此完成构建。
    此种实现,较为简单清晰,但是在实现过程中,由于实现路径写入错误,极有可能导致堆栈溢出。而且涉及文件结构处理,不可避免会涉及复杂结构,内存压力和堆栈溢出的风险这两个问题都需要考虑。所以,在老大Review完代码后,提出改写为迭代的方式进行创建。

使用迭代创建文件树形结构

  • 在树形结构的基础上,我们对构建方法改造为使用迭代方式进行实现。

  • 迭代构建代码

/**
 * called by root to create the file struct
 * @param nodeMap
 */
public void buildFileSys(Map<String, FileItem> nodeMap) {
    if (nodeMap == null || nodeMap.size() < 1) {
        return;
    }
    for (Map.Entry<String, FileItem> entrySource : nodeMap.entrySet()) {
        String pathKey = entrySource.getKey();
        if (TextUtils.isEmpty(pathKey)) {
            continue;
        }
        DecompressFileItem pathItem = (DecompressFileItem) entrySource.getValue();
        if (pathItem == null) {
            continue;
        }
        for (Map.Entry<String, FileItem> entryCompare : nodeMap.entrySet()) {
            DecompressFileItem compareItem = (DecompressFileItem) entryCompare.getValue();
            if (compareItem == null) {
                continue;
            }
            boolean isRootNode = compareItem.mData.equals(mData) && compareItem.mFileName.equals(mFileName);
            if (pathKey.equals(compareItem.getParentPath()) && !isRootNode) {
                if (pathItem.mChildList == null) {
                    pathItem.mChildList = new ArrayList<FileItem>();
                }
                pathItem.mChildList.add(compareItem);
                // build file count
                if (pathItem.mIsDir) {
                    pathItem.mFileCount++;
                }
            }
        }
    }
}
  • 代码总结与分析
    此种构建方式相比于方式嵌套了两层循环,增加了代码的阅读难度,但是相对来说,还是比较简单,同时时间复杂度也只有O(n * n),相对来说还是一种不错的选择。
    同时在遍历的过程中,尝试去将一个结点所有子节点添加完毕后,就移除这个结点,可以减少时间复杂度,但是对于无序输入的结点,这样做,可能会出现,某个子节点已经被删除了,但它还未添加到其父结点中。╮(╯▽╰)╭
    如果大家对于构建类似的结构还有其他更好的建议,请大家不吝赐教(∩_∩)

最后,附上银魂的一句吐槽:压力是导致秃顶的原因,所以请注意不要压力太大,但这样一来反而容易堆积压力,所以归根到底我们无能为力」

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • B树的定义 一棵m阶的B树满足下列条件: 树中每个结点至多有m个孩子。 除根结点和叶子结点外,其它每个结点至少有m...
    文档随手记阅读 13,012评论 0 25
  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,660评论 0 19
  • 基于树实现的数据结构,具有两个核心特征: 逻辑结构:数据元素之间具有层次关系; 数据运算:操作方法具有Log级的平...
    yhthu阅读 3,943评论 1 5
  • 1.树(Tree): 树是 n(n>=0) 个结点的有限集。当 n=0 时称为空树。在任意一颗非空树中:有且仅有一...
    ql2012jz阅读 928评论 0 3
  • (一)十三年前 “回来的路上,看见一个大叔骑车摔倒了。”阿农脱下湿漉漉的外套,“雨下得太大了,想来是滑倒了吧。” ...
    未予先闻阅读 246评论 0 0