使用 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 特性为字符串创建一个字符计数数组的主要内容,如果未能解决你的问题,请参考以下文章