如何使用Lambda正确制作多级地图?

时间:2019-07-25 10:19:46

标签: java lambda java-stream collectors

我正在使用JCIFS比较文件夹(接收方和发送方)中的文件。比较期间可能会发生两种情况: -文件在接受者处不存在 -文件存在于接收器上

我需要得到一张地图,其中比较文件按提及的两种类型分组,因此我可以复制不存在的文件或文件大小以及现有文件的修改日期...

我想使用lambda和流来实现它,因为我将在不久的将来使用并行流,这也很方便... \

我设法制作了一个可行的原型方法,用于检查文件是否存在并创建地图:

    private Map<String, Boolean> compareFiles(String[] acceptor, String[] sender) {

        return Arrays.stream(sender)
                .map(s -> new AbstractMap.SimpleEntry<>(s, Stream.of(acceptor).anyMatch(s::equals)))
Map.Entry::getValue)));
                .collect(collectingAndThen(
                        toMap(Map.Entry::getKey, Map.Entry::getValue),
                        Collections::<String,Boolean> unmodifiableMap));
    }

但是我无法通过地图值添加更高级别的分组...

我有这么一段无效的代码:

    private Map<String, Boolean> compareFiles(String[] acceptor, String[] sender) {

        return Arrays.stream(sender)
                .map(s -> new AbstractMap.SimpleEntry<>(s, Stream.of(acceptor).anyMatch(s::equals)))
                .collect(groupingBy(
                        Map.Entry::getValue,
                        groupingBy(Map.Entry::getKey, Map.Entry::getValue)));
    }
}

我的代码无法编译,因为我错过了一些非常重要的内容。有人可以帮我解释一下如何使此lambda正确吗?

P.S。方法参数的数组是SmbFiles samba目录:

private final String master = "smb://192.168.1.118/mastershare/";
private final String node = "smb://192.168.1.118/nodeshare/";

SmbFile masterDir   = new SmbFile(master);
SmbFile nodeDir     = new SmbFile(node);

Map<Boolean, <Map<String, Boolean>>> resultingMap = compareFiles(masterDir, nodeDir);

2 个答案:

答案 0 :(得分:1)

收集到具有相同值的嵌套地图不是很有用。结果Map<Boolean, Map<String, Boolean>>只能有两个键truefalse。当您在其上调用get(true)时,会得到一个Map<String, Boolean>,其中所有字符串键都冗余地映射到true。同样,get(false)将为您提供一个所有值均为false的you map。

在我看来,你确实想要

private Map<Boolean, Set<String>> compareFiles(String[] acceptor, String[] sender) {
    return Arrays.stream(sender)
        .collect(partitioningBy(Arrays.asList(acceptor)::contains, toSet()));
}

其中get(true)为您提供了一组所有字符串,其中谓词评估为true,反之亦然。

partitioningBygroupingBy键的boolean的优化版本。

请注意,Stream.of(acceptor).anyMatch(s::equals)是Stream功能的过度使用。 Arrays(acceptor).contains(s)更简单,并且在用作Arrays.asList(acceptor)::contains之类的谓词时,表达式Arrays.asList(acceptor)只会被求值一次,并且每次求值时调用contains的函数都会传递给收集器

acceptor变大时,您不应该考虑并行处理,而应将线性搜索替换为哈希查找

private Map<Boolean, Set<String>> compareFiles(String[] acceptor, String[] sender) {
    return Arrays.stream(sender)
        .collect(partitioningBy(new HashSet<>(Arrays.asList(acceptor))::contains, toSet()));
}

同样,new HashSet<>(Arrays.asList(acceptor))的准备工作仅完成一次,而为contains的每个元素完成的sender调用将不取决于{{1}的大小}。

答案 1 :(得分:0)

我设法解决了我的问题。我的类型不匹配,因此工作代码为:

private Map<Boolean, Map<String, Boolean>> compareFiles(String[] acceptor, String[] sender) {

        return Arrays.stream(sender)
                .map(s -> new AbstractMap.SimpleEntry<>(s, Stream.of(acceptor).anyMatch(s::equals)))
                .collect(collectingAndThen(
                        groupingBy(Map.Entry::getValue, toMap(Map.Entry::getKey, Map.Entry::getValue)),
                        Collections::<Boolean, Map<String, Boolean>> unmodifiableMap));
    }