Videos
Files.walk(Paths.get("/path/to/stuff/"))
.filter(p -> p.toString().endsWith(".ext"))
.map(p -> p.getParent().getParent())
.distinct()
.forEach(System.out::println);
This filters all files that have the extension and gets the parent path of their directory. distinct ensures that every path is used only once.
You are invoking the method printIfArtifactVersionDirectory for all visited directories. I did a little change to make it obvious:
static void printIfArtifactVersionDirectory(Path path) {
System.out.println("--- " + path);
...
}
With that additional output you will get:
--- 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\stuff\org\foo\bar
--- C:\Projects\stuff\org\foo\bar\1.2.4
C:\Projects\stuff\org\foo\bar
--- C:\Projects\stuff\org\foo\bar\blah
--- C:\Projects\stuff\org\foo\bar\blah\2.1
C:\Projects\stuff\org\foo\bar\blah
--- C:\Projects\stuff\org\foo\bar\blah\2.2
C:\Projects\stuff\org\foo\bar\blah
So you get the output as often as you have artifact version directories. If you want to remember that you already did the output for one directory, you must make store this information somewhere. One quick implementation could be:
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);
}
}
}
With this you get the expected output:
C:\Projects\stuff\org\foo\bar
C:\Projects\stuff\org\foo\bar\blah
A better solution would be to use the set for storing the visited parents and only print them after visiting them all:
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);
}
}
Usage:
PathStore pathStore = new PathStore();
Files.walk(Paths.get("/path/to/stuff/"))
.filter(Files::isDirectory)
.forEach(pathStore::visit);
pathStore.print();
I am writing a simple program that walks the file tree to generate various statistics about the files.
For example:
try (Stream<Path> walk = Files.walk(PATH)) {
// Find all directories
List<Path> dirs = walk.filter(Files::isDirectory).collect(Collectors.toList());
// Find all files
List<Path> files = walk.filter(Files::isRegularFile).collect(Collectors.toList());
// Find zip archive files
List<Path> zips = walk.filter(
p -> p.getFileName().toString().toLowerCase().endsWith(".zip"))
.collect(Collectors.toList());
// Find files bigger than 1 Mb
List<Path> filesBiggerThan1Mb = walk.filter(p -> {
try {
return Files.size(p) > 1048576;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}).collect(Collectors.toList());
// Get total size of all files
long totalSize = walk.filter(Files::isRegularFile).mapToLong(p -> {
try {
return Files.size(p);
} catch (IOException e) {
e.printStackTrace();
return 0;
}
}).sum();
}Currently it walks the file tree multiple times by reusing the walk object. Although it seems like either the JRE or os does some caching in memory, and subsequent Files walks are much faster, I am wondering how I can write it in a different way to only need to invoke Files walk only once and do everything in 1 sweep.
I receive IllegalStateException.
That's because you've closed the stream before you try to access anything from it. A try-with-resources in the getStream method will close the stream when that method returns; so you won't be able to read the stream in getList.
Put the try-with-resources in getList:
public List<String> getList(Path dir) {
try (Stream<Path> stream = getStream(dir)) {
return stream
.map(filename -> filename.toString())
.collect(Collectors.toList());
}
}
You can use the try with resources if you don't use a separate method for getStream() :
public List<String> getList(Path dir) {
if ( Files.exists(dir)) {
try (Stream<Path> stream = Files.walk(dir, 1)) {
return stream
.filter(Files::isRegularFile)
.map(dir::relativize).map(filename -> filename.toString())
.collect(Collectors.toList());
} catch ( IOException ioe) {
System.out.println("ERROR: " + ioe.getMessage());
}
}
return null;
}
The try-with resources closes the Stream as soon as the try-with-resources block is done, which is causing your issue.
In the above example, all remains within the try-with-resources block, so the Stream can still be used to find the List you want.