【问题标题】:How can I properly make a multilevel map using lambdas?如何使用 lambdas 正确制作多级地图?
【发布时间】:2019-07-25 10:19:46
【问题描述】:

我正在使用 JCIFS 比较文件夹(收件人和发件人)中的文件。在比较过程中可能会出现两种情况: - 文件不存在于接受者 - 文件存在于acceptor

我需要一张地图,其中比较的文件按上述两种类型分组,所以我可以复制不存在的文件或检查现有文件的大小和修改日期...

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

我已经设法制作了一个可以检查文件是否存在并创建地图的工作原型方法:

    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 正确?

附:方法参数中的数组是 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);

【问题讨论】:

    标签: java lambda java-stream collectors


    【解决方案1】:

    收集到具有相同值的嵌套映射不是很有用。生成的 Map&lt;Boolean, Map&lt;String, Boolean&gt;&gt; 只能有两个键,truefalse。当你在上面调用get(true) 时,你会得到一个Map&lt;String, Boolean&gt;,其中所有的字符串键都冗余映射到true。同样,get(false) 会给你一个映射,其中所有值都是false

    对我来说,看起来你真的想要

    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&lt;&gt;(Arrays.asList(acceptor)) 的准备工作只进行一次,而对sender 的每个元素进行的contains 调用将不再依赖于acceptor 的大小。

    【讨论】:

      【解决方案2】:

      我已经设法解决了我的问题。我的类型不匹配,所以工作代码是:

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

      【讨论】:

        猜你喜欢
        • 2011-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-02
        • 1970-01-01
        • 2022-08-16
        相关资源
        最近更新 更多