如何简洁地收集流到地图?

时间:2018-02-23 16:40:16

标签: java

给定一个目录名列表,我想生成目录中所有文件到其内容的映射。

有关更明确实施的建议吗?

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

    Arrays.stream(filenames)
            .map(s -> FileUtils.listFiles(new File(rootdir, s), new RegexFileFilter(".*"), DirectoryFileFilter.DIRECTORY))
            .flatMap(Collection::stream)
            .forEach(f -> {
                try {
                    map.put(f, FileUtils.readFileToString(f));
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            });

    return map;

`

3 个答案:

答案 0 :(得分:3)

您应该使用Collectors.toMap

Map<File, String> map = Arrays.stream(filenames)
    .map(s -> FileUtils.listFiles(new File(rootdir, s), new RegexFileFilter(".*"), DirectoryFileFilter.DIRECTORY))
    .flatMap(Collection::stream)
    .collect(Collectors.toMap(
        Function.identity(),
        f-> {
            try { return FileUtils.readFileToString(f); }
            catch (IOException ioe) { return null; }
        }
    ));

它的第一个参数是键映射函数。未修改的文件应该用作键,因此我们可以使用标识功能 它的第二个参数是值映射函数,我们将File映射到它们的内容。

请注意,我的值映射函数会生成null个值,并且在无法读取文件的内容时不会输出任何错误。考虑使用其他答案之一,除非您确信IOException永远不会发生。

答案 1 :(得分:2)

你的做法真的不错,但我想知道在你的情况下使用流是否是获得更明确代码的最佳方式。
Aaron解决方案稍微更直,更“流式”,因为它将地图收集为终端操作,但它也会在抛出IOException的情况下更改原始逻辑。

没有流的版本显得非常直接:

Map<File, String> map = new HashMap<>();
for (String filename : filenames) {           
    for (File file : FileUtils.listFiles(new File(rootdir, filename), new RegexFileFilter(".*"), DirectoryFileFilter.DIRECTORY)) {
        try {
            map.put(file, FileUtils.readFileToString(file));
        } catch (IOException e) {
            // log exception
        }
    }
}

编辑:tsolakp与stream的解决方案非常好,即使它比使用经典循环的方式更不直接。

这是一个较轻的版本 我认为不需要在流内部使用Optional。它是“我们的”流,所以我们掌握了我们放入的内容(这里null表示失败案例) 此外,包括map()处理中的代码可能看起来不那么“功能”但它仍然是可读的,我发现它更适合阅读间接。

import java.util.AbstractMap.SimpleImmutableEntry;
// ...
Map<File, String> map = Arrays.stream(filenames)
        .flatMap( s -> FileUtils.listFiles( new File(rootdir, s), new RegexFileFilter(".*"), DirectoryFileFilter.DIRECTORY ).stream() )  
        .map( f -> {                    
                    try{ 
                        return new SimpleImmutableEntry<>(f, FileUtils.readFileToString(f) ); 
                    }catch(IOException e){
                        //log exception
                        return null; 
                    }                        
            }
         )
        .filter(Objects::nonNull)
        .collect(Collectors.toMap(SimpleImmutableEntry::getKey, SimpleImmutableEntry::getValue));

答案 2 :(得分:1)

“更明确的实施”是基于意见的。对于我使用像@davidxxx这样的普通循环来说,确实是首选方式。 当然,您也可以使用Java流,但是缺少处理异常将使您的代码使用try / catch语句难看。 一种选择是将try / catch移动到单独的方法或实用程序类中,并使流表达式更清晰,如下所示:

Map<File, String> map = Arrays.stream(filenames)
.flatMap( s -> FileUtils.listFiles( new File(rootdir, s), new RegexFileFilter(".*"), DirectoryFileFilter.DIRECTORY ).stream() )  
.map( f -> new AbstractMap.SimpleImmutableEntry<>(f, read(f) ) )
.filter( e -> e.getValue().isPresent() )
.collect( Collectors.toMap( e -> e.getKey(), e -> e.getValue().get() ) );

private Optional<String> read(File f){
    try{ 
        return Optional.of( FileUtils.readFileToString(f) ); 
    }catch(IOException e){
        //log exception
        return Optional.empty();
    }
}