2015-09-30 14 views
6

特定のディレクトリ内のファイルを見つける:私はこのようなFiles.walkFileTreeを使用して大規模かつ深くネストされたディレクトリ構造を反復:はEfficently私は簡単な問題を抱えている

final int CUTOFF = 5; 
final List<Path> foundList = new ArrayList<>(); 
Files.walkFileTree(codeRoot, new SimpleFileVisitor<Path>() { 
    @Override 
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) 
      throws IOException { 
     String rPath = codeRoot.relativize(dir).toString(); 
     int level = rPath.length() - rPath.replace("/", "").length(); 
     if (dir.getFileName().toString().equals("target") || level < CUTOFF) { 
      return FileVisitResult.CONTINUE; 
     } 
     return FileVisitResult.SKIP_SUBTREE; 
    } 
    @Override 
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
      throws IOException { 
     if (file.getFileName().toString().endsWith(".txt")) { 
      foundList.add(file); 
     } 
     return FileVisitResult.CONTINUE; 
    } 
}); 

私の目標は、特定のディレクトリtargetの下にあるすべてのファイルを追加することであるI知っているのはCUTOFFレベルでcodeRootです。

これは、必要な場合には、stat()コールまたは「完了できません」という声で、より効率的な方法を探しています。

言語レベルはJava8です。

+0

なぜそれはできると思いますか? walkFileTreeはNIOを使用します。これは、パフォーマンス面でネイティブウォークと同じくらいよくなることを意味します。これを頻繁に呼び出すと、いくつかのキャッシュを使用できます。キャッシュの例:最後の呼び出し以降に変更されていないディレクトリをキャッシュする(一部のファイルシステムでは)ディレクトリの最終変更時刻。 –

+0

@MladenAdamovic私は主に、アルゴリズムのショートカットがないと思っていました。また、 'relativize()'が避けることができるfsの性能に影響を与えるかどうかは分かりません。繰り返し実行の最適化についてのあなたの考えは良いものです、ありがとう! – mabi

+0

あなたはスピードの尺度として何を使用していますか?参照点としてC/C++で同様のソリューションを実装しましたか?これまでのところ、それはなぜ非効率だと思いますか? – Fallso

答えて

1

提示されたアルゴリズムはワンタイムクエリです。この場合、すべてのディレクトリで線形時間の検索が行われます。そのように各ディレクトリを調べる必要性を最小限に抑えることはできません。もちろん、キャッシュを見ることはできますが、キャッシュの一貫性に気を配り、高いパフォーマンスが必要な場合は、インデックスを作成することも考えられます。どちらの場合も、質問した質問に対処します。これは1回限りのクエリです。

使用しているFiles.walkFileTreeのバージョンでは、最大レベルを超えたすべてのファイルとディレクトリを含むツリー全体が表示されます。パス名を解析することで明示的に除外しています。効率的でないかもしれないと正しく考えているテクニックです。解決方法は、常にドキュメントを読むことです。明示的な引数として最大深度を持つFiles.walkFileTreeの2番目のバージョンがあります。 tutorial on walking the file treeから:

二walkFileTreeメソッドを使用すると、さらに訪問したレベルの数とFileVisitOptionの列挙型のセットに制限を指定することができます。

2番目の方法を使用する場合は、候補ファイルを最大レベルで訪問するだけで、サブツリーをプルーニングするすべてのコードを回避できます。

+0

余分な方法についてよく聞きます。これは 'walkFileTree'実装を見てみました。これは' stack'を使って訪問するディレクトリを追跡します。 'SKIP_SUBTREE'が返されるとスタック要素がポップされます。*これは(このディレクトリの新しいスタックエントリを生成しないことによって)これ以上のトラバースを終了する必要がありますか?だからあなたは2つが同等であると言っていますが、 'maxDepth'バリアントを使用すると、私は手動の深さの計算をカットできますか? – mabi

+0

@mabi 'SKIP_SUBTREE'が行う操作は、通常、「プルーニング」と呼ばれます。現在のノードでのトラバーサルを停止し、すべての子ノードでのトラバーサルを回避し、サブツリーが横断された場合にのみ続行します。そう、はい、この行動の分析は正しいです。 2番目の質問では、 'maxDepth'を使用する実装は実際に深さを追跡しています(実際には、スタックのサイズであるため)。ヒント:誰かがあなたのために書いたコードは絶対に書かないでください。 – eh9

+0

フェアポイント。 「あなたはもっとやることはできません」と「最適化の余地がある」の両方のポイントをヒットしたので、誰かがそれを前に吹き飛ばさない限り、賞金EODを授与します。 – mabi

1

最適化オプション:

1)ディレクトリの変更時に通知を登録:https://docs.oracle.com/javase/tutorial/essential/io/notification.html これは背景

2で動作することができます)(以下、最適な)いくつかのファイルシステムでは変更されていないディレクトリ()のキャッシュを使用します。最後の呼び出し以降に変更されていないディレクトリをキャッシュするディレクトリの最終変更時刻を使用する

grepcodeを使用すると、相対化がどのように実装されているかわかりませんでした。私はそれが既に引っ張られた値の単純な文字列操作で実装されていると思うし、私はそれがstat()にアクセスしているとは思わない。 relativizeの有無にかかわらずダミーコード(何も役に立たない)を作成してテストしたり、多数のファイルをトラバースするときに実際の影響を測定したりすることができます。あなたが多くのパフォーマンスを失うことはないと確信することができますrelativize

+0

'relativize()'はJVM + OSに依存しますが、私の場合は 'sun.nio.fs.UnixPath'を介して実装されています。逆コンパイルされたコードはちょっと追跡が難しいです。 – mabi

+0

テストコードを作成します(これは、何も役立たずにディレクトリをたどります)。相対的なパフォーマンスが+ 30%低下した場合は、その修正方法を見つけようとするべきです –

関連する問題