2016-08-03 8 views
0

私のコードはファイルツリーオブジェクトの多くのパスを解析します。効率的なアルゴリズムはありますか?

dir1/file1 
dir1/dir2/file2 
dir1/dir2/file3 

FileTreeオブジェクトの可視化の一例として、多くのファイルパスのファイルツリーの作成が必要です。

dir1 
|_file1 
|_dir2 
    |_file2 
    |_file3 

このツリーは、グラフ形式で急流コンテンツファイルの可視化のために使用されています。また、ファイルの進行状況を動的に表示するためにも使用されます。 少数のサブフォルダとファイルでは効果的ですが、パスが10,000を超えるとメモリと時間がかかります(> 4秒と50 MB RAM)。

このようなグラフを作成するための効率的なアルゴリズムはありますか?私にとって最も重要なのはグラフ作成速度です。 アルゴリズム実装の例は、どの言語で書いても構いませんが、私にとっては関係ありません:-) ありがとうございます。

私のこの目的のためのJavaコード:

FileTree root = new FileTree(FileTree.ROOT, File.Type.DIR); 
FileTree parentTree; 

for (String pathToFile : paths) { 
    parentTree = root; 
    String[] nodes = FileIOUtils.parsePath(pathToFile); /*String.split(File.separator)*/ 

    for (int i = 0; i < nodes.length; i++) { 
      /* The last leaf item is a file */ 
     if (i == (nodes.length - 1)) { 
      parentTree.addChild(new FileTree(nodes[i], 
       File.Type.FILE, parentTree)); 
     } else { 
      parentTree.addChild(new FileTree(nodes[i], FileNode.Type.DIR, parentTree)); 
     } 

     FileTree nextParent = parentTree.getChild(nodes[i]); 
      /* Skipping leaf nodes */ 
     if (nextParent != null && !nextParent.isFile()) { 
      parentTree = nextParent; 
     } 
    } 
} 

FileTreeクラス:

public class FileTree { 
    public static final String ROOT = "/"; 
    /* The name for pointer to the parent node */ 
    public static final String PARENT_DIR = ".."; 

    protected String name; 
    protected boolean isLeaf; 
    protected FileTree parent; 
    protected Map<String, FileTree> children = new LinkedHashMap<>(); 

    public FileTree(String name, int type, FileTree parent) { 
     this(name, type, parent); 
    } 

    public FileTree(String name, int type) 
    { 
     this(name, type, null); 
    } 

    public FileTree(String name, int type, FileTree parent) 
    { 
     this.name = name; 
     isLeaf = (type == File.Type.FILE); 
     this.parent = parent; 
    } 

    public synchronized void addChild(FileTree node) 
    { 
     if (!children.containsKey(node.getName())) { 
      children.put(node.getName(), node); 
     } 
    } 

    public boolean contains(String name) 
    { 
     return children.containsKey(name); 
    } 

    public F getChild(String name) 
    { 
     return children.get(name); 
    } 

    public Collection<FileTree> getChildren() 
    { 
     return children.values(); 
    } 

    public Set<String> getChildrenName() 
    { 
     return children.keySet(); 
    } 
} 

編集:

1000個のサブフォルダのツリーを作成するの速度を実現することが可能であったAN平均は0.5-1秒(早い30秒)です。

FileTree root = new BencodeFileTree(FileTree.ROOT, 0L, File.Type.DIR); 
    FileTree parentTree = root; 
    /* It allows reduce the number of iterations on the paths with equal beginnings */ 
    String prevPath = ""; 
    /* Sort reduces the returns number to root */ 
    Collections.sort(files); 

    for (String file : files) { 
     String path; 
     /* 
     * Compare previous path with new path. 
     * Example: 
     * prev = dir1/dir2/ 
     * cur = dir1/dir2/file1 
     *  |________| 
     *   equal 
     * 
     * prev = dir1/dir2/ 
     * cur = dir3/file2 
     *  |________| 
     *   not equal 
     */ 
     if (!prevPath.isEmpty() && 
       file.regionMatches(true, 0, prevPath, 0, prevPath.length())) { 
      /* 
      * Beginning paths are equal, remove previous path from the new path. 
      * Example: 
      * prev = dir1/dir2/ 
      * cur = dir1/dir2/file1 
      * new = file1 
      */ 
      path = file.substring(prevPath.length()); 
     } else { 
      /* Beginning paths are not equal, return to root */ 
      path = file; 
      parentTree = root; 
     } 

     String[] nodes = FileIOUtils.parsePath(path); 
     /* 
     * Remove last node (file) from previous path. 
     * Example: 
     * cur = dir1/dir2/file1 
     * new = dir1/dir2/ 
     */ 
     prevPath = file.substring(0, file.length() - nodes[nodes.length - 1].length()); 

     /* Iterates path nodes */ 
     for (int i = 0; i < nodes.length; i++) { 
      if (!parentTree.contains(nodes[i])) { 
       /* The last leaf item is a file */ 
       parentTree.addChild(makeObject(nodes[i], parentTree, 
           i == (nodes.length - 1))); 
      } 

      FileTree nextParent = parentTree.getChild(nodes[i]); 
      /* Skipping leaf nodes */ 
      if (!nextParent.isFile()) { 
       parentTree = nextParent; 
      } 
     } 
    } 
+0

ようになり提案ループ本体の後

parentTree = parentTree.addChild(... 

に置き換えることができます。さまざまな使用シナリオを異なる方法で最適化することができます。 –

+0

このツリーは、トレントコンテンツファイルをグラフィカル形式で視覚化するために使用されます。また、ファイルの進行状況を動的に表示するためにも使用されます。 – proninyaroslav

答えて

0

基本的なアルゴリズムは、私にはよさそうだが、あなたはすぐに彼らはすでに存在している(共通)の場合には捨てられますaddChildを呼び出すときは、不要なFileTree多数のオブジェクトを作成しています。あなたは、コンストラクタにパラメータを渡して試してみて、それを挿入する必要がある場合にのみ、オブジェクトを構築することができます:

public synchronized void addChild(String name, int type, FileTree parent) 
{ 
    if (!children.containsKey(name)) { 
     children.put(name, new FileTree(name, type, parent)); 
    } 
} 

とを:それはparentTreeに合格する必要はないかもしれません

if (i == (nodes.length - 1)) { 
    parentTree.addChild(nodes[i], File.Type.FILE, parentTree); 
} else { 
    parentTree.addChild(nodes[i], FileNode.Type.DIR, parentTree); 
} 

:あなたができますthisでそれを構成してください。

Stringオブジェクト(および関連するFileTreeノード)の配列を処理した前のパスから維持し、子を追加する前に以前のエントリと異なるエントリが見つかるまでスキャンすることもできます。

+0

ありがとうございます。私はFileTreeから子チェックを削除します: 'if(parentTree.contains(nodes [i])){...}'。 ハッシュマップを使って子(キー:ファイル名、値:FileTree)を格納するので、子の存在をツリーで確認できるので、「Stringオブジェクトの配列(および関連するFileTreeノード)」を理解できません。 私はパスリストを与える静的メソッドで別のクラスにツリーを作成します。 – proninyaroslav

+0

'string nodes []'を処理するあなたのforループで、 'string prevNodes []'とdir1/dir2/dir3/dir4/dir5/file1'とそれに続いて 'dir1/dir2/dir3/dir4/dir6/file1'があれば、dir1ではなくdir6で処理を開始することができます。しかし、少しの利益のためにあまりにも複雑すぎます。PSはあなたが既にスピードに差をつけた変更を行いましたか? – samgak

+0

私はあなたのオプションに似たようなことを考えました、ツリー内の名前の繰り返しが考えられます。約2〜2.5倍で改善される(たとえば、1000個のサブフォルダを持つツリーと各フォルダに10個のファイル - ツリー時間を2.5秒(5秒早く)にする。ここに私のコードがあります(それは未加工ですが、チェック用に作られています)[http://pastebin.com/PwQ3sprD](http://pastebin.com/PwQ3sprD) – proninyaroslav

0

LinkedHashMapHashMapに置き換えることをお勧めします。これは、最初にメモリを消費するためです。主な違いは、HashMapはエントリに対する反復の順序を保証しないことです。しかし、あなたはGUIで子供たちを注文することができます(おそらくあなたは何とか注文設定をしています)。いくつかの参考文献については、this questionをご覧ください。


もう一つの提案は、再び地図上

FileTree nextParent = parentTree.addChild(... 

とをgetをコールする必要はありませんループ内でメソッド次にaddChild

public synchronized FileTree addChild(FileTree node) { 
    return children.putIfAbsent(node.getName(), node); 
} 

から実際の子ノードを返すようになります見えない状態があります

if (nextParent != null && !nextParent.isFile()) { 
    parentTree = nextParent; 
} 

現在の子がファイルの場合、繰り返しのループがないように見えます。だから、安全にあなたがそれを使用することが起こっているかを説明してもらえ

for(...) { 
    int type = if (i == (nodes.length - 1)) ? File.Type.FILE : FileNode.Type.DIR; 
    parentTree = parentTree.addChild(new FileTree(nodes[i], type, parentTree); 
} 
関連する問題