为 IntStream 生成直方图 Map 引发编译时错误
Producing histogram Map for IntStream raises compile-time-error
我有兴趣构建霍夫曼编码原型。为此,我想首先生成构成输入 Java String
的字符的直方图。我在 SO 和其他地方看到过很多解决方案(例如: 依赖于对 Stream
使用 collect()
方法以及 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
方式对于我想要做的事情来说可能是太多的样板;如果是这样请告知。
String.chars()
方法 returns 和 IntStream
。您可能想通过以下方式将其转换为 Stream<Character>
:
s.chars().mapToObj(c -> (char)c)
如前所述,您可以将流转换为原始类型再转换为对象类型。
s.chars().boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
问题是 s.chars()
returns 一个 IntStream
- Stream
的一个特定专业,它没有一个 collect
需要一个争论;它的 collect
有 3 个参数。显然,您可以使用 boxed
并将 IntStream
转换为 Stream<Integer>
。
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<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}
在 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}
我有兴趣构建霍夫曼编码原型。为此,我想首先生成构成输入 Java String
的字符的直方图。我在 SO 和其他地方看到过很多解决方案(例如:Stream
使用 collect()
方法以及 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
方式对于我想要做的事情来说可能是太多的样板;如果是这样请告知。
String.chars()
方法 returns 和 IntStream
。您可能想通过以下方式将其转换为 Stream<Character>
:
s.chars().mapToObj(c -> (char)c)
如前所述,您可以将流转换为原始类型再转换为对象类型。
s.chars().boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
问题是 s.chars()
returns 一个 IntStream
- Stream
的一个特定专业,它没有一个 collect
需要一个争论;它的 collect
有 3 个参数。显然,您可以使用 boxed
并将 IntStream
转换为 Stream<Integer>
。
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<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}
在 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}