[Java]過去の自分が作成した機能をjava.io.FileからNIO.2で書き直し

# 背景

Javaでファイルやディレクトリ操作をしたいと思った場合、お世話になるクラスは以下が一般的かと思います。

- java.io.File
- java.nio.Path
- java.nio.Files
- java.nio.file.Paths

Fileクラスは昔からあります。
NIO.2は、Java 7で導入されたクラスで、Fileクラスに比べて新しいです。

なので、新規で実装する場合は、NIO.2を使用したいものです。

今回は、自分がjava.io.Fileで書いたコードを、NIO.2で書き直したものを紹介します。

# 紹介する機能

- 指定されたディレクトリ内にファイルが存在するか
- 指定されたディレクトリを中身も含めて全削除

# 指定されたディレクトリ内にファイルが存在するか

## java 6以前

```java
import java.io.File;

public class Demo {
    public static void main(String[] args) {
        File file = new File("C:/temp/");

        // ファイル名の一覧を取得し、存在チェック
        if (file.listFiles().length == 0) {
            System.out.println("ファイルはありません。");
        } else {
            System.out.println("ファイルがあります。");
        }
    }
}
```

File.listFiles()の戻り値は、File型の配列で返されます。
そのlenghで配列の長さを見て、判定するという流れです。

## java 7以降

```java
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Demo {
    public static void main(String[] args) {
        Path path = Paths.get("C:/temp/");

        try {
            if (Files.list(path).findAny().isPresent()) {
                System.out.println("ファイルはあります。");
            } else {
                System.out.println("ファイルはありません。");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```
```Files.list``` は、ディレクトリ内のエントリを取得してStremで返します。
```findAny()``` は、最初に見つけた要素をOptionalで返します。
```isPresent()``` は、Optionalが保持する値がnullならばfalse、nullじゃなかったらtrueを返します。

# 指定されたディレクトリを中身も含めて全削除

## java 6以前

```java
import java.io.File;

public class Demo {
    public static void main(String[] args) {
        File dir = new File("C:/temp/");
        deleteDir(dir);
    }

    public static void deleteDir(File dir) {
        if (dir.exists() && dir.isDirectory()) {
            for (File child : dir.listFiles()) {
                if (child.isDirectory()) {
                    deleteDir(child);
                } else {
                    child.delete();
                }
            }
        }
        dir.delete();
    }
}
```
## java 8以降

**注意！** Streamを使用しているので、java8以降でしか動作しません。

```java
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;

public class Demo {
    public static void main(String[] args) {
        Path path = Paths.get("C:/temp/");

        // ディレクトリを全削除
        try {
            Files.walk(path).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
            path.toFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

```Files.walk(path)``` で、IOExceptionが発生する可能性があるので、throwするかcatchで対応してください。
```Files.walk(path)``` は、サブディレクトリを含む一覧を取得しています。
```sorted(Comparator.reverseOrder())``` は、降順に並び替えます。
こうすると、ファイルが先頭にきて、ディレクトリは後ろのほうに並び替えられます。
そして、filterして抽出したPaths型をFiles型に変換し、ループで削除…ということをしています。
（ごめんなさい。うまく説明できていないかもしれません。）

下記のようにすれば、```Stream<Path>``` を ```List<Path>``` に変換することができます。
```.collect(Collectors.toList())``` を追加するだけですが…

```java
List<Path> list = Files.walk(path).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
for (Path p : list) {
    System.out.println(p);
}
```


# おまけ

試行錯誤していたら、たまたまできた機能です。
せっかくなので、紹介します。

## 指定されたディレクトリ内のファイル削除

## java 8以降

**注意！** Streamを使用しているので、java8以降でしか動作しません。

```java
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Demo {
    public static void main(String[] args) {
        Path path = Paths.get("C:/temp/");

        // ディレクトリ内にあるファイルを削除(サブディレクトリは削除しない)
        try {
            Files.walk(path).filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

```.filter(Files::isRegularFile)``` で、通常ファイルかどうかをチェックしています。
これでディレクトリは削除対象から除外されます。

# 最後に

いまだにJava 8のラムダ式、Stream、Optionalなどに慣れないです。
時代遅れ感がすごいので、なんとかしたい。けど調べれば調べるほど分からなくなって沼にハマっていきます…