在Java字符串中查找字符出现的次数

20

我想要统计一个字符串中某个字符出现的次数,例如我有字符串 "aaaab",我该如何计算其中字母"a"的数量?


8
看起来您给这个问题打上了 regex 标签。记住,有些人面对问题会想:“嘿,我来用正则表达式解决!”现在他们有两个问题了。 - Greg Hewgill
@Greg 只有当人们不恰当地使用正则表达式(就像在这个问题中一样)时,才会出现这个问题。 - NullUserException
任何解决方案都可以,但我对在正则表达式中看到一个解决方案很感兴趣。 - Steffan Harris
如何计算字符串中字符出现的次数? - Jonik
16个回答

23

Guava 的 CharMatcher API 功能强大而简洁:

CharMatcher.is('a').countIn("aaaab"); //returns 4

22
String string = "aaab";
int count = string.length() - string.replaceAll("a", "").length();

使用正则表达式如"[a-zA-Z]"来计算所有单词字符,而不是使用"a"


15

请注意,StringUtils将在另一个字符串中查找字符串的出现,因此可能不如使用特定于字符的搜索效率高。 - dty
2
为简洁和易读性加1。 - Mark Thomas
@MikeG,请更新链接:http://commons.apache.org/proper/commons-lang//apidocs/org/apache/commons/lang3/StringUtils.html - Yura Shinkarev

14

如果你不使用正则表达式,代码看起来会更容易阅读。

int count = 0;
for(int i =0; i < string.length(); i++)
    if(string.charAt(i) == 'a')
        count++;

count 现在包含您字符串中 'a' 的数量。而且,这种方法的执行时间是最优的。

正则表达式适用于模式匹配,但在这里使用普通循环即可完成任务。


'jjnguy' Nelson:你的(被接受的)答案只适用于计算Java字符。它不能处理Java字符串可能包含的所有Unicode字符。String的*codePointAt(...)是你要寻找的方法,而不是charAt(...)*,因为自从Unicode 3.1发布以来,它已经失效了。 - SyntaxT3rr0r
@Web,能否给我提供一个参考资料?我很想学习更多相关知识。 - jjnguy
'jjnguy' Nelson: 我认为JavaDoc非常详尽(不确定是否这样说)。基本上,charAt 返回16位值,并且自Unicode 3.1 / Java 1.5以来,Unicode(和Java)支持超过65536个字符。因此,charAt 可以返回一个不是Unicode字符的“东西”。新的codePointAt 返回32位值,因此可以包含所有有效的Unicode字符。 - SyntaxT3rr0r
1
@Web,好的。这很有道理。我以为16位就足够了...不过我还是会保留我的答案。添加那个陌生的方法对于刚接触该语言的人来说并没有帮助。而且,你下面的评论指出了代码中的缺陷。 - jjnguy

5

正则表达式并不擅长计算简单的事情。可以想象成用大锤去捣蚂蚁。但是它们擅长将复杂的字符串拆分成几个部分。

无论如何,这里有一个解决方案,OP感兴趣的是使用正则表达式来计算'a'的数量:

public class Reggie {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("[^a]*a");
        Matcher matcher = pattern.matcher("aaabbbaaabbabababaaabbbbba");
        int count =  0;
        while(matcher.find()) {
            count++;
        }
        System.out.println(count+" matches");
    }
}

这种方法相对较慢,正如其他人所指出的那样。更糟糕的是,它并不是最简单的方法,也不太可能没有错误。但如果您需要比“a”更复杂的东西,则正则表达式将变得更加适用,特别是当所需字符串越来越复杂时。例如,如果您想从长字符串中挑选美元金额,则正则表达式可能是最佳答案。
现在,让我们谈谈正则表达式:[^a]*a 这个[^a]*意味着“匹配零个或多个非“a”字符”。这使我们能够从字符串开头吞噬非“a”垃圾:如果输入是“bbba”,则[^a]*将匹配“bbb”,而不是匹配“a”。不要担心,正则表达式中的尾随“a”表示“匹配一个'a'”。因此,我们的正则表达式是:“匹配零个或多个非'a'字符,后跟一个'a'。”
好了。现在您可以阅读有关Pattern和Matcher的内容。简而言之,Pattern是编译的正则表达式。编译正则表达式很昂贵,因此我将我的编译为静态,以便它们只编译一次。Matcher是一个类,它将字符串应用于Pattern以查看是否匹配。Matcher具有状态信息,可以让它爬行下字符串并重复应用Pattern。
循环基本上是说:“匹配器,向下爬行字符串,找到模式的下一个出现。如果我们找到了它,请增加计数器。”请注意,Matcher找到的字符序列不仅仅是“a”。它找到的是像以下这样的序列:“a”、“bbba”、“bba”、“ba”等不包含“a”的字符串,除了它们的最后一个字符。

4
int count = 0;
for (char c : string.toCharArray()) 
    if (c == 'a')
        count++;

1
@dty 但是垃圾回收器会处理它。除非你的字符串很大,我觉得这不是什么大问题。 - Aillyn
@dty,Java中的超低延迟系统,我明白了。不管怎样,现在已经过了我的睡觉时间,现在是离开的好时机,我想 :) - Bart Kiers
2
我对那些在SO上谈论使用Java进行低延迟系统开发的人数感到吃惊。这就像用汇编语言进行跨平台开发一样——有时你只是在用错误的工具来完成工作。 - Yishai
2
错的工具怎么会呢?我不能透露细节,但我们可以通过我们专有的可靠中间件和几个服务器跳点从我们的边界获取数万条消息,并在单毫秒延迟下持续地、没有显著延迟峰值地将它们返回到边界,使用普通硬件和一个单线程架构,其中包括完整的热备份和日志记录。那么这到底是什么样的错误工具呢? - dty
3
我对那些说Java不能用于编写高性能系统的人感到惊讶,仅仅是因为他们自己无法编写高性能代码! :-) - dty
显示剩余6条评论

3
      String searchFor = "a";
      String base = "aaaab";
      int count=0;
      int index =base.indexOf(searchFor);

      while(index!=-1){
          ++count;
          index = base.indexOf(searchFor, index+searchFor.length());
      }

      System.out.println(count);

3
一个简单的字符循环可以实现它。
public int countChars(char c, String s) {
  int result = 0;
  for (int i = 0, n = s.length(); i < n; i++) {
    if (s.charAt(i) == c) {
      result++;
    }
  }
  return result;
}

FYI:任何体面的JRE的JIT都会为您将i < s.length()for (int i = 0; i < s.length(); i++)中移出来:通常没有必要通过这样的“优化”使代码更难阅读。这是一篇关于“聪明”的编程技巧的好文章:写愚蠢的代码 - 四位领先的Java开发者的建议 - Bart Kiers
作为一种模式,这可以避免你考虑限制表达式是否是编译器可以优化/是否是常量。例如,以这种方式编写代码,可以避免我思考 for (int i = 0; i < expensiveCalculation(); i++)... 是否真的很昂贵和/或常量和/或可以提升出循环。 - dty
虽然在这种简单的情况下我同意,但其实并不需要它。 - dty

3

以下是一种不需要任何额外库的非常简短的解决方案:

String input = "aaaab";

int i = -1, count = 0;
while( (i = input.indexOf( 'a', i + 1 ) ) != -1 ) count++;

System.out.println( count );

2
public static void main(String[] args) {

    Map<Character, Integer> data = new HashMap<Character, Integer>();

    String s = "aaaab";

    char[] chars = s.toCharArray();
    for (char a : chars) {

        if (data.containsKey(a)) {
            int value = data.get(a);
            data.put(a, value + 1);
        } else {
            data.put(a, 1);
        }

    }
    Iterator it = data.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry pairs = (Map.Entry) it.next();
        System.out.println(pairs.getKey() + " = " + pairs.getValue());
    }
}

数据包含什么?为什么containsKey会起作用! - Kumaran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接