使用 java 8 特性为字符串创建一个字符计数数组

Posted

技术标签:

【中文标题】使用 java 8 特性为字符串创建一个字符计数数组【英文标题】:Create a char count array for a String using java8 features 【发布时间】:2020-07-27 17:48:48 【问题描述】:

我希望使用 Java 8 流 API 为特定字符串(仅包含小写英文字母)创建一个 int[] 计数。其中 arr[i] 表示英语词典中第 i 个字符的计数(例如,arr[0] = String str 中 'a' 的计数,而 arr[2] = String str 中'c' 的计数。这可以很简单完成者:

int[] arr = new int[26];
for(char c : str.toCharArray())
       arr[c-'a']++;

或者以第二种方式使用 IntSream:

int[] arr = IntStream.range('a','z'+1).map(i -> (int)str.chars().filter(c -> c == i).count()).toArray();

但是第二种方法的问题是,对于从 'a' 到 'z' 的每个字符,String 被遍历了 26 次

您能否建议使用 java8-stream API 实现相同目标的更好方法?

PS:我知道这可以使用 Map 来完成,但我需要 int[]

【问题讨论】:

您的迭代方法要好得多。不要使用流。保留你所拥有的。 【参考方案1】:
int[] r = str.chars()
             .boxed()
             .reduce(new int[26], 
                     (a, c) ->  ++a[c - 'a']; return a; , 
                     (a1, a2) -> a1);

你知道前者更简单更好。我的回答只是证明使用 Stream API 是可行的,并不建议您使用它。就个人而言,我会选择地图方法作为最直观的方法。

正如@Holger 所指出的,collect 在这里是一个更好的选择

str.chars()
   .map(c -> c - 'a')
   .collect(() -> new int[26], 
            (a, i)-> a[i]++, 
            (a1, a2) -> /* left as an exercise to the reader*/);

【讨论】:

我会在.chars() 之后抛出.filter(i -> i >= 'a' && i <= 'z'),以防有人在str 中使用超出范围的字符调用它,以避免潜在的越界异常。 @DavidConrad 我同意,但 OP 似乎对 new int[26] 很有信心,也许 str 在此之前正在得到验证 在缩减函数中修改传入参数是对 API 的滥用。显然,这将与并行流中断。这是collect 的完美用例,甚至可以在不装箱的情况下完成,str.chars().map(c -> c - 'a').collect(() -> new int[26], (a,i)->a[i]++, (a1, a2) -> /* left as an exercise to the reader*/)【参考方案2】:

如果您想使用流并保持迭代方法,您也可以这样做:

final int count[] = new int[26];
test.chars().forEach(c -> count[c-'a']++);

【讨论】:

我发现这行得通。你能解释一下为什么这不违反 lambda 函数的 "Effective final" 子句吗? 由于数组是java中的对象,所以count数组实际上是最终的;它的引用在迭代期间不会改变,即。它没有分配给新对象。即使您更改对象的值(成员),它也会保持最终状态。

以上是关于使用 java 8 特性为字符串创建一个字符计数数组的主要内容,如果未能解决你的问题,请参考以下文章

js小数字换算科学计数法

将具有科学计数法的字符串列表转换为十进制数

if语句检查文本框中的字符数

Java 的版本历史与特性

如何在C中使用pthread_join来控制线程数?

js 科学计数法 转换为 数字字符 突破幂数正数21位,负数7位的自动转换限制