У вас есть несколько вопросов с вашим вопросом.
Например, что я нашел:
- Предположение
- Как было указано в ответе Хемант Патель ,
files.get(0)
просто возвращает первого ребенка. Если точно не получится, если нет детей.
- Кроме того, я никогда не использовал Apache Commons IO, но оказывается, что
DirectoryFileFilter
принимает только каталоги, и вам нужно TrueFileFilter
, как указано в документации Java.
- Память и общие проблемы с производительностью
- Я не уверен, как Apache Commons IO работает за кулисами, но возвращение коллекции в памяти,
LinkedList
, вероятно, проблема с памятью. Например, Google Guava fileTreeTraverser
возвращает traverser, который позволяет перебирать итерируемое, не требуя сохранения всего списка в памяти (однако для Iterable
потребовалась бы пользовательская пара сериализатор / десериализатор и обнаружение каталог войти / выйти в любом случае).
- Сериализация потенциально большой коллекции в строку с использованием
String Gson.toJson(...)
- еще одна проблема с памятью. Если возможно, вы должны рассмотреть потоковую передачу.
- Проблемы совместимости
- Я бы не рекомендовал сериализовать
File
напрямую, потому что Gson не предоставляет для этого пару сериализатор / десериализатор. Начиная с последней версии Gson 2.8.4 он использует ReflectiveTypeAdapterFactory$Adapter
, поэтому он использует File
поля. Если внутренняя структура File
по какой-либо причине изменится, вы не сможете ее десериализовать. Кроме того, наличие одного свойства path
не позволяет различать каталоги и файлы.
Сказав это, вы, возможно, захотите пройтись по каталогу файлов самостоятельно, преобразовав каждый каталог и файл в любое представление, которое вы хотите использовать, как можно меньшего количества файлов.
Приведенный ниже интерфейс позволит вам построить любую структуру JSON. Например:
- Супер-простой плоский список:
[
"./file1",
"./file2",
"./dir1",
"./dir1/file1",
"./dir1/file2",
"./dir2",
"./dir2/file1"
]
- Типизированный плоский список:
[
{"type": "file", "path": "./file1"},
{"type": "file", "path": "./file2"},
{"type": "directory", "path": "./dir1"},
{"type": "file", "path": "./dir1/file1"},
{"type": "file", "path": "./dir1/file2"},
{"type": "directory", "path": "./dir2"},
{"type": "file", "path": "./dir2/file1"}
]
- Объект JSON каталога / файла, где объект указывает каталог, тогда как
null
указывает файл:
{
"file1": null
"file2": null,
"dir1": {
"file1": null,
"file2": null
},
"dir2": {
"file1": null
}
}
- JSON-массив каталогов / файлов, где строка указывает файл, а объект указывает каталог:
[
"file1"
"file2",
{
"name": "dir1",
"children": [
"file1",
"file2"
]
},
{
"name": "dir2",
"children": [
"file1"
]
}
]
interface IDirectoryWalkListener {
void onEnterDirectory(int level, @Nonnull File directory)
throws IOException;
void onFile(@Nonnull File file)
throws IOException;
void onLeaveDirectory(int level, @Nonnull File directory)
throws IOException;
}
final class DirectoryWalk {
private DirectoryWalk() {
}
static void walk(final File root, final IDirectoryWalkListener listener)
throws IOException {
walk(0, root, listener);
}
private static void walk(final int level, final File root, final IDirectoryWalkListener listener)
throws IOException {
if ( !root.isDirectory() ) {
throw new IOException(root + " must be a directory");
}
@Nullable
final File[] files = root.listFiles();
if ( files == null ) {
throw new IOException("Cannot list files in " + root);
}
listener.onEnterDirectory(level, root);
for ( final File file : files ) {
if ( file.isDirectory() ) {
walk(level + 1, file, listener);
} else {
listener.onFile(file);
}
}
listener.onLeaveDirectory(level, root);
}
}
final class ToFlatJsonArrayDirectoryWalkListener
implements IDirectoryWalkListener {
private final JsonWriter jsonWriter;
private ToFlatJsonArrayDirectoryWalkListener(final JsonWriter jsonWriter) {
this.jsonWriter = jsonWriter;
}
static IDirectoryWalkListener get(final JsonWriter jsonWriter) {
return new ToFlatJsonArrayDirectoryWalkListener(jsonWriter);
}
@Override
public void onEnterDirectory(final int level, @Nonnull final File directory)
throws IOException {
if ( level == 0 ) {
jsonWriter.beginArray();
}
jsonWriter.value(directory.getPath());
}
@Override
public void onFile(@Nonnull final File file)
throws IOException {
jsonWriter.value(file.getPath());
}
@Override
public void onLeaveDirectory(final int level, @Nonnull final File directory)
throws IOException {
if ( level == 0 ) {
jsonWriter.endArray();
}
}
}
Пример использования:
// Writing to a string is a potential performance and memory issue
final Writer out = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(out);
jsonWriter.setIndent("\t");
DirectoryWalk.walk(root, ToFlatJsonArrayDirectoryWalkListener.get(jsonWriter));
System.out.println(out);
final Writer out = new OutputStreamWriter(System.out) {
@Override
public void close() {
// do not close System.out
}
};
final JsonWriter jsonWriter = new JsonWriter(out);
jsonWriter.setIndent("\t");
DirectoryWalk.walk(root, ToFlatJsonArrayDirectoryWalkListener.get(jsonWriter));
out.flush();
Пример вывода, если root
равно ./target
:
[
"./target",
"./target/data",
"./target/data/journal",
"./target/data/journal/server.lock",
"./target/classes",
"./target/classes/com",
"./target/classes/com/google",
"./target/classes/com/google/gson",
"./target/classes/com/google/gson/interceptors",
"./target/classes/com/google/gson/interceptors/InterceptorFactory$InterceptorAdapter.class",
"./target/classes/com/google/gson/interceptors/InterceptorFactory$1.class",
"./target/classes/com/google/gson/interceptors/InterceptorFactory.class",
"./target/classes/com/google/gson/interceptors/JsonPostDeserializer.class",
"./target/classes/com/google/gson/interceptors/Intercept.class",
...
]
Чтобы восстановить структуру файлов из JSON, вам потребуется JsonReader
(для более эффективного использования памяти) или пользовательский десериализатор (если вы хорошо читаете все данные в память).