【问题标题】:Producing histogram Map for IntStream raises compile-time-error为 IntStream 生成直方图会引发编译时错误
【发布时间】:2017-12-03 23:17:53
【问题描述】:

我对构建 Huffman Coding 原型很感兴趣。为此,我想首先生成构成输入 Java String 的字符的直方图。我已经在 SO 和其他地方看到了许多解决方案(例如:here 依赖于使用 collect()Streams 方法以及 Function.identity()Collectors.counting() 的静态导入以非常具体和直观的方式.

但是,当使用一段与我上面链接的代码非常相似的代码时:

private List<HuffmanTrieNode> getCharsAndFreqs(String s){
        Map<Character, Long> freqs = s.chars().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        return null;
}

我从 Intellij 收到一个编译时错误,它基本上告诉我 collect 没有符合 Supplier 类型的参数,正如其签名所要求的那样:

不幸的是,我是 Java 8 Stream 层次结构的新手,我不完全确定对我来说最好的做法应该是什么。事实上,采用Map 的方式对于我正在尝试做的事情来说可能是太多的样板;如果是,请告知。

【问题讨论】:

    标签: java-8 grouping histogram java-stream aggregation


    【解决方案1】:

    String.chars() 方法返回一个IntStream。您可能希望通过以下方式将其转换为 Stream&lt;Character&gt;

    s.chars().mapToObj(c -> (char)c)
    

    【讨论】:

      【解决方案2】:

      如前所述,您可以将流转换为原始类型到对象类型。

      s.chars().boxed()
       .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
      

      【讨论】:

        【解决方案3】:

        问题在于s.chars() 返回一个IntStream——Stream 的一个特殊特化,它没有一个接受单个参数的collect;它是 collect 需要 3 个参数。显然您可以使用boxed,这会将IntStream 转换为Stream&lt;Integer&gt;

        Map<Integer, Long> map = yourString.codePoints()
                  .boxed()
                  .collect(Collectors.groupingBy(
                              Function.identity(), 
                              Collectors.counting()));
        

        但现在的问题是您计算了code-points 而不是字符。如果您绝对知道您的字符串是由 BMP 中的字符组成的,您可以安全地转换为 char,如另一个答案所示。如果你不是 - 事情会变得更棘手。

        在这种情况下,您需要将单个 unicode 代码点作为字符获取 - 但它可能不适合 Java char - 有 2 个字节;一个unicode字符最多可以有4个字节。

        在这种情况下,您的地图应该是 Map&lt;String, Long&gt; 而不是 Map&lt;Character, Long&gt;

        在 java-9 中引入了支持的 \X(和 Scanner#findAll),这很容易做到:

         String sample = "A" + "\uD835\uDD0A" + "B" + "C";
                 Map<String, Long> map = scan.findAll("\\X")
                       .map(MatchResult::group)
                       .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        
        
         System.out.println(map); // {A=1, B=1, C=1, ?=1}
        

        在 java-8 中这会有点冗长:

            String sample = "AA" + "\uD835\uDD0A" + "B" + "C";
            Map<String, Long> map = new HashMap<>();
        
            Pattern p = Pattern.compile("\\P{M}\\p{M}*+");
            Matcher m = p.matcher(sample);
        
            while (m.find()) {
                map.merge(m.group(), 1L, Long::sum);
            }
            System.out.println(map); // {A=2, B=1, C=1, ?=1}
        

        【讨论】:

        • 如果通过codePoints() 创建的流将计算代码点而不是chars 是一个问题,那么,就像OP 一样使用chars()。您正在尝试解决您自己创建的问题。
        • @Holger 嗯.. 你可能是对的。所以,如果我想查看“人类可读字符”以及重复多少次,我可以简单地使用:s.codePoints().mapToObj(x -&gt; new String(Character.toChars(x)))
        • “人类可读字符”是另一回事。有chars,这些 16 位单元可以将 1:1 或 2:1 映射到 代码点,但是(人类可读的)字符甚至可能由多个代码点,如 U+0041 U+0308,都在 BMP ,因此不要将“char 与代码点”问题与“代码点与字符(或字素簇)”问题混为一谈,这一点很重要.您的最后两个示例拆分为 characters,但如果您想打开那罐蠕虫,您必须注意它们不了解哪些字符是相同的,例如U+0041 U+0308U+00C4 相同…
        • 但请注意,OP 似乎打算实现一种不需要关心语义的压缩算法,因此char 值的直方图就足够了。不要惊讶,an older answer 已经有类似的东西了 ;^)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-29
        相关资源
        最近更新 更多