Как использовать Files.walk()... для получения графика файлов на основе условий?
У меня есть следующие структуры каталогов:
/path/to/stuff/org/foo/bar/
/path/to/stuff/org/foo/bar/1.2.3/
/path/to/stuff/org/foo/bar/1.2.3/myfile.ext
/path/to/stuff/org/foo/bar/1.2.4/
/path/to/stuff/org/foo/bar/1.2.4/myfile.ext
/path/to/stuff/org/foo/bar/blah/
/path/to/stuff/org/foo/bar/blah/2.1/
/path/to/stuff/org/foo/bar/blah/2.1/myfile.ext
/path/to/stuff/org/foo/bar/blah/2.2/
/path/to/stuff/org/foo/bar/blah/2.2/myfile.ext
Я хотел бы получить следующий вывод:
/path/to/stuff/org/foo/bar/
/path/to/stuff/org/foo/bar/blah/
У меня есть следующий код (ниже), который неэффективен, поскольку он печатает:
/path/to/stuff/org/foo/bar/
/path/to/stuff/org/foo/bar/
/path/to/stuff/org/foo/bar/blah/
/path/to/stuff/org/foo/bar/blah/
Вот код Java:
public class LocatorTest
{
@Test
public void testLocateDirectories()
throws IOException
{
long startTime = System.currentTimeMillis();
Files.walk(Paths.get("/path/to/stuff/"))
.filter(Files::isDirectory)
.forEach(Foo::printIfArtifactVersionDirectory);
long endTime = System.currentTimeMillis();
System.out.println("Executed in " + (endTime - startTime) + " ms.");
}
static class Foo
{
static void printIfArtifactVersionDirectory(Path path)
{
File f = path.toAbsolutePath().toFile();
List<String> filePaths = Arrays.asList(f.list(new MyExtFilenameFilter()));
if (!filePaths.isEmpty())
{
System.out.println(path.getParent());
}
}
}
}
Фильтр:
public class MyExtFilenameFilter
implements FilenameFilter
{
@Override
public boolean accept(File dir, String name)
{
return name.endsWith(".ext");
}
}
Ответы
Ответ 1
Files.walk(Paths.get("/path/to/stuff/"))
.filter(p -> p.toString().endsWith(".ext"))
.map(p -> p.getParent().getParent())
.distinct()
.forEach(System.out::println);
Фильтрует все файлы с расширением и получает родительский путь к каталогу. distinct
гарантирует, что каждый путь используется только один раз.
Ответ 2
Вы вызываете метод printIfArtifactVersionDirectory
для всех посещенных каталогов. Я сделал небольшое изменение, чтобы сделать его очевидным:
static void printIfArtifactVersionDirectory(Path path) {
System.out.println("--- " + path);
...
}
С этим дополнительным выходом вы получите:
--- C:\Projects\stuff
--- C:\Projects\stuff\org
--- C:\Projects\stuff\org\foo
--- C:\Projects\stuff\org\foo\bar
--- C:\Projects\stuff\org\foo\bar\1.2.3
C:\Projects\вещи\орг\Foo\бар
--- C:\Projects\stuff\org\foo\bar\1.2.4
C:\Projects\вещи\орг\Foo\бар
--- C:\Projects\stuff\org\foo\bar\blah
--- C:\Projects\stuff\org\foo\bar\blah\2.1
C:\Projects\вещи\орг\Foo\бар\бла
--- C:\Projects\stuff\org\foo\bar\blah\2.2
C:\Projects\stuff\org\foo\bar\blah
Таким образом, вы получаете результат так часто, как у вас есть каталоги версий артефакта. Если вы хотите запомнить, что вы уже сделали вывод для одного каталога, вы должны где-то хранить эту информацию. Быстрая реализация может быть:
static class Foo {
private static final Set<Path> visited = new HashSet<>();
static void printIfArtifactVersionDirectory(Path path) {
...
Path parent = path.getParent();
if (!filePaths.isEmpty() && !visited.contains(parent)) {
visited.add(parent);
System.out.println(parent);
}
}
}
При этом вы получите ожидаемый результат:
C:\Projects\вещи\орг\Foo\бар
C:\Projects\stuff\org\foo\bar\blah
Лучшим решением было бы использовать набор для хранения посещенных родителей и печатать только после их посещения:
static class PathStore {
private final Set<Path> store = new HashSet<>();
void visit(Path path) {
File f = path.toAbsolutePath().toFile();
List<String> filePaths = Arrays.asList(f.list(new MyExtFilenameFilter()));
if (!filePaths.isEmpty()) {
store.add(path.getParent());
}
}
void print() {
store.forEach(System.out::println);
}
}
Использование:
PathStore pathStore = new PathStore();
Files.walk(Paths.get("/path/to/stuff/"))
.filter(Files::isDirectory)
.forEach(pathStore::visit);
pathStore.print();