Javaのアプリ

コンソールではなくGUIで対話式のインターフェースを提供するために、JavaのSwingを使用します。以下に、Swingを用いて対話式のファイル比較ツールを実装したコードを示します。

### Swingを用いた対話式ファイル比較ツール

```java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;

public class FileComparator extends JFrame {
    private static final int THREAD_POOL_SIZE = 4;  // Intel i5の4コアに合わせて設定
    private static final int BUFFER_SIZE = 8192;

    private JTextField sourceFolderPathField;
    private JTextField targetFolderPathField;
    private JTextArea outputArea;
    private JButton compareButton;

    public FileComparator() {
        setTitle("ファイル比較ツール");
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        JPanel inputPanel = new JPanel();
        inputPanel.setLayout(new GridLayout(3, 2));

        inputPanel.add(new JLabel("ソースフォルダのパス:"));
        sourceFolderPathField = new JTextField();
        inputPanel.add(sourceFolderPathField);

        inputPanel.add(new JLabel("ターゲットフォルダのパス:"));
        targetFolderPathField = new JTextField();
        inputPanel.add(targetFolderPathField);

        compareButton = new JButton("比較開始");
        inputPanel.add(compareButton);

        add(inputPanel, BorderLayout.NORTH);

        outputArea = new JTextArea();
        outputArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(outputArea);
        add(scrollPane, BorderLayout.CENTER);

        compareButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String sourceFolderPath = sourceFolderPathField.getText();
                String targetFolderPath = targetFolderPathField.getText();

                if (!isValidFolder(sourceFolderPath) || !isValidFolder(targetFolderPath)) {
                    outputArea.append("提供されたフォルダパスが無効です。\n");
                    return;
                }

                outputArea.append("比較を開始します...\n");
                long startTime = System.currentTimeMillis();
                compareFolders(sourceFolderPath, targetFolderPath);
                long endTime = System.currentTimeMillis();
                outputArea.append("処理時間: " + (endTime - startTime) + " ミリ秒\n");
            }
        });
    }

    private boolean isValidFolder(String folderPath) {
        File folder = new File(folderPath);
        return folder.isDirectory();
    }

    private void compareFolders(String sourceFolderPath, String targetFolderPath) {
        File sourceFolder = new File(sourceFolderPath);
        File targetFolder = new File(targetFolderPath);

        File[] sourceFiles = sourceFolder.listFiles();
        File[] targetFiles = targetFolder.listFiles();

        Map<String, File> sourceFileMap = new HashMap<>();
        Map<String, File> targetFileMap = new HashMap<>();

        if (sourceFiles != null) {
            for (File file : sourceFiles) {
                sourceFileMap.put(file.getName(), file);
            }
        }

        if (targetFiles != null) {
            for (File file : targetFiles) {
                targetFileMap.put(file.getName(), file);
            }
        }

        Set<String> allFileNames = new HashSet<>();
        allFileNames.addAll(sourceFileMap.keySet());
        allFileNames.addAll(targetFileMap.keySet());

        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        List<Future<?>> futures = new ArrayList<>();

        for (String fileName : allFileNames) {
            Future<?> future = executorService.submit(() -> {
                File sourceFile = sourceFileMap.get(fileName);
                File targetFile = targetFileMap.get(fileName);

                try {
                    if (sourceFile == null) {
                        // 追加データ
                        writeToFile(targetFile, "updatedData.txt", "追加データ: " + fileName);
                        outputArea.append("追加データ: " + fileName + "\n");
                    } else if (targetFile == null) {
                        // 削除データ
                        writeToFile(sourceFile, "deletedData.txt", "削除データ: " + fileName);
                        outputArea.append("削除データ: " + fileName + "\n");
                    } else {
                        // 差分比較
                        compareFiles(sourceFile, targetFile, "updatedData.txt");
                    }
                } catch (IOException e) {
                    outputArea.append("ファイル処理中のエラー: " + fileName + "\n");
                    e.printStackTrace();
                }
            });
            futures.add(future);
        }

        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                outputArea.append("並行実行中のエラー\n");
                e.printStackTrace();
            }
        }

        executorService.shutdown();
    }

    private void compareFiles(File sourceFile, File targetFile, String outputFileName) throws IOException {
        try (BufferedReader sourceReader = new BufferedReader(new FileReader(sourceFile), BUFFER_SIZE);
             BufferedReader targetReader = new BufferedReader(new FileReader(targetFile), BUFFER_SIZE)) {

            String sourceLine;
            String targetLine;
            boolean differencesFound = false;

            while ((sourceLine = sourceReader.readLine()) != null && (targetLine = targetReader.readLine()) != null) {
                if (!sourceLine.equals(targetLine)) {
                    writeToFile(sourceFile, outputFileName, "更新あり: " + sourceFile.getName());
                    outputArea.append("更新あり: " + sourceFile.getName() + "\n");
                    differencesFound = true;
                    break;
                }
            }

            if (!differencesFound && (sourceReader.readLine() != null || targetReader.readLine() != null)) {
                writeToFile(sourceFile, outputFileName, "更新あり: " + sourceFile.getName());
                outputArea.append("更新あり: " + sourceFile.getName() + "\n");
            }
        }
    }

    private void writeToFile(File file, String outputFileName, String header) throws IOException {
        synchronized (FileComparator.class) {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFileName, true), BUFFER_SIZE);
                 BufferedReader reader = new BufferedReader(new FileReader(file), BUFFER_SIZE)) {
                writer.write(header);
                writer.newLine();

                String line;
                while ((line = reader.readLine()) != null) {
                    writer.write(line);
                    writer.newLine();
                }
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            FileComparator fileComparator = new FileComparator();
            fileComparator.setVisible(true);
        });
    }
}
```

### 説明

1. **GUIの構築**：
    - `JFrame`を拡張して、ファイル比較ツールのウィンドウを作成します。
    - テキストフィールド（`JTextField`）を使用して、ユーザーがソースフォルダとターゲットフォルダのパスを入力できるようにします。
    - 結果を表示するためのテキストエリア（`JTextArea`）を配置し、スクロール可能にします。

2. **イベントハンドリング**：
    - 比較ボタン（`JButton`）にアクションリスナーを追加し、ボタンがクリックされたときにファイル比較処理を開始します。
    - ユーザーが入力したフォルダパスの有効性をチェックし、無効な場合はエラーメッセージを表示します。

3. **ファイル比較処理**：
    - 前述のコンソール版と同様に、フォルダ内のファイルをマップに格納し、並行処理で比較を行います。
    - 比較結果は、テキストエリアにリアルタイムで表示されます。

このGUI版ツールにより、ユーザーは対話的にフォルダパスを入力し、ファイル比較の結果を直感的に確認することができます。