Java 如何使用 Files.walk()... 根据条件获取文件图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29574167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 08:14:57  来源:igfitidea点击:

How to use Files.walk()... to get a graph of files based on conditions?

javajava-8

提问by carlspring

I have the following directory structures:

我有以下目录结构:

/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

I would like to get the following output:

我想得到以下输出:

/path/to/stuff/org/foo/bar/
/path/to/stuff/org/foo/bar/blah/

I have the following code (below), which is inefficient, as it prints out:

我有以下代码(如下),这是低效的,因为它打印出来:

/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/

Here is the Java code:

这是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());
            }
        }

    }

}

The filter:

过滤器:

public class MyExtFilenameFilter
        implements FilenameFilter
{

    @Override
    public boolean accept(File dir, String name)
    {
        return name.endsWith(".ext");
    }

}

采纳答案by a better oliver

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. distinctensures that every path is used only once.

这将过滤所有具有扩展名的文件并获取其目录的父路径。distinct确保每条路径只使用一次。

回答by Seelenvirtuose

You are invoking the method printIfArtifactVersionDirectoryfor all visited directories. I did a little change to make it obvious:

您正在printIfArtifactVersionDirectory为所有访问过的目录调用该方法。我做了一些改动以使其显而易见:

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

--- 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 rememberthat 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

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 aftervisiting them all:

更好的解决方案是使用 set 来存储访问过的父母,并访问完所有父母后才打印它们:

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();

回答by cfstras

Adding to @a-better-oliver's answer, here is what you can do if your action declares IOExceptions.

添加到@a-better-oliver 的回答中,如果您的操作声明了 IOExceptions,您可以执行以下操作。

You can treat the filtered stream as an Iterable, and then do your action in a regular for-each loop. This way, you don't have to handle exceptions inside a lambda.

您可以将过滤后的流视为Iterable,然后在常规 for-each 循环中执行您的操作。这样,您就不必在 lambda 中处理异常。

try (Stream<Path> pathStream = Files
        .walk(Paths.get("/path/to/stuff/"))
        .filter(p -> p.toString().endsWith(".ext"))
        .map(p -> p.getParent().getParent())
        .distinct()) {

    for (Path file : (Iterable<Path>) pathStream::iterator) {
        // something that throws IOException
        Files.copy(file, System.out);
    }
}

Found that trick here: https://stackoverflow.com/a/32668807/1207791

在这里找到这个技巧:https: //stackoverflow.com/a/32668807/1207791