为 IntStream 生成直方图会引发编译时错误

Jas*_*son 3 grouping histogram aggregation java-8 java-stream

我对构建霍夫曼编码原型感兴趣。为此,我想首先生成组成输入 Java 的字符的直方图String。我已经在 SO 和其他地方看到了许多解决方案(例如:这里依赖于使用s的collect()方法Stream以及以非常具体和直观的方式使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

Intellij 的错误信息

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

Eug*_*ene 7

问题是s.chars()返回一个IntStream- 一个特殊的特化Stream并且它没有一个collect接受单个参数的;它collect需要 3 个参数。显然,您可以使用boxed,这会将其转换IntStreamStream<Integer>.

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

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

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

在这种情况下,您的地图应该是Map<String, Long>而不是Map<Character, Long>

在 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}
Run Code Online (Sandbox Code Playgroud)

在 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}
Run Code Online (Sandbox Code Playgroud)

  • “人类可读的字符”是另一回事。有 `char`s,这些 16 位单元可以将 1:1 或 2:1 映射到*代码点*,但是(人类可读的)*字符*甚至可能由多个代码点组成,例如 `U+0041 U+ 0308`,两者都在*内* BMP,因此重要的是不要将“`char` vs 代码点”问题与“代码点 vs 字符(或字素簇)”问题混淆。你的最后两个例子分成了*个字符*,但是如果你想打开那罐蠕虫,你必须注意它们不知道哪些字符是相同的,例如`U+0041 U+0308`与` U+00C4`… (3认同)
  • 但请注意,OP 似乎打算实现一种不需要关心语义的压缩算法,因此 `char` 值的直方图就足够了。并且不要惊讶已经有 [一个较旧的答案](/sf/answers/2330204621/) 有类似的东西;^) (2认同)