1. 序章

この記事では、プレーンJavaでディレクトリを再帰的に削除する方法を説明します。 また、外部ライブラリを使用してディレクトリを削除するためのいくつかの代替案についても見ていきます。

2. ディレクトリを再帰的に削除する

Javaには、ディレクトリを削除するオプションがあります。 ただし、これにはディレクトリが空である必要があります。 したがって、再帰を使用して、特定の空でないディレクトリを削除する必要があります。

  1. 削除するディレクトリのすべてのコンテンツを取得します
  2. ディレクトリではないすべての子を削除します(再帰から終了します)
  3. 現在のディレクトリのサブディレクトリごとに、手順1(再帰的な手順)から開始します。
  4. ディレクトリを削除します

この単純なアルゴリズムを実装しましょう。

boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}

この方法は、簡単なテストケースを使用してテストできます。

@Test
public void givenDirectory_whenDeletedWithRecursion_thenIsGone() 
  throws IOException {
 
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

    boolean result = deleteDirectory(pathToBeDeleted.toFile());

    assertTrue(result);
    assertFalse(
      "Directory still exists", 
      Files.exists(pathToBeDeleted));
}

テストクラスの@Beforeメソッドは、 pathToBeDeleted の場所にサブディレクトリとファイルを含むディレクトリツリーを作成し、@Afterメソッドは必要に応じてディレクトリをクリーンアップします。

次に、最も一般的に使用される2つのライブラリApacheのcommons-ioとSpringFrameworkのspringコアを使用して削除を実現する方法を見てみましょう。これらのライブラリはどちらも、1行のコードを使用してディレクトリを削除できます。

3. commons-ioFileUtilsを使用する

まず、commons-io依存関係をMavenプロジェクトに追加する必要があります。

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

依存関係の最新バージョンはここにあります。

これで、 FileUtils を使用して、 deleteDirectory()を含む任意のファイルベースの操作を1つのステートメントで実行できます。

FileUtils.deleteDirectory(file);

4. SpringからFileSystemUtilsを使用する

または、s pring-core依存関係をMavenプロジェクトに追加することもできます。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.10.RELEASE</version>
</dependency>

依存関係の最新バージョンはここにあります。

FileSystemUtilsdeleteRecursively()メソッドを使用して、削除を実行できます。

boolean result = FileSystemUtils.deleteRecursively(file);

Javaの最近のリリースでは、次のセクションで説明するこのようなIO操作を実行する新しい方法が提供されています。

5. Java7でのNIO2の使用

Java 7では、ファイルを使用してファイル操作を実行するまったく新しい方法が導入されました。 これにより、ディレクトリツリーをトラバースし、実行するアクションにコールバックを使用できます。

public void whenDeletedWithNIO2WalkFileTree_thenIsGone() 
  throws IOException {
 
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

    Files.walkFileTree(pathToBeDeleted, 
      new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult postVisitDirectory(
          Path dir, IOException exc) throws IOException {
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
        
        @Override
        public FileVisitResult visitFile(
          Path file, BasicFileAttributes attrs) 
          throws IOException {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }
    });

    assertFalse("Directory still exists", 
      Files.exists(pathToBeDeleted));
}

Files.walkFileTree()メソッドは、ファイルツリーをトラバースし、イベントを発行します。 これらのイベントのコールバックを指定する必要があります。 したがって、この場合、 SimpleFileVisitor を定義して、生成されたイベントに対して次のアクションを実行します。

  1. ファイルへのアクセス–ファイルを削除します
  2. エントリを処理する前にディレクトリにアクセスする–何もしない
  3. エントリの処理後にディレクトリにアクセスする-このディレクトリ内のすべてのエントリがこれまでに処理(または削除)されているため、ディレクトリを削除します
  4. ファイルにアクセスできません–失敗の原因となったIOExceptionを再スローしてください

ファイル操作の処理に関するNIO2APIの詳細については、JavaNIO2ファイルAPIの概要を参照してください。

6. NIO2とJava8の使用

Java 8以降、Stream APIは、ディレクトリを削除するさらに優れた方法を提供します。

@Test
public void whenDeletedWithFilesWalk_thenIsGone() 
  throws IOException {
    Path pathToBeDeleted = TEMP_DIRECTORY.resolve(DIRECTORY_NAME);

    Files.walk(pathToBeDeleted)
      .sorted(Comparator.reverseOrder())
      .map(Path::toFile)
      .forEach(File::delete);

    assertFalse("Directory still exists", 
      Files.exists(pathToBeDeleted));
}

ここで、 Files.walk()は、逆の順序で並べ替えたPathStreamを返します。 これにより、ディレクトリ自体の前にディレクトリの内容を示すパスが配置されます。 その後、マップしますファイルそれぞれを削除しますファイル。

7. 結論

このクイックチュートリアルでは、ディレクトリを削除するさまざまな方法について説明しました。 再帰を使用して削除する方法を見ながら、いくつかのライブラリ、イベントを活用するNIO2、関数型プログラミングパラダイムを採用するJava8パスストリームについても見てきました。

この記事のすべてのソースコードとテストケースは、GitHubから入手できます。