如何在字符串中计算字符出现的次数?

621
我有一个字符串
a.b.c.d
我希望以惯用方式计算“.”的出现次数,最好是一行代码解决。
(之前我曾表达过这个限制条件为“不使用循环”,以防你想知道为什么每个人都试图回答而不使用循环)。

1
作业?否则我看不出避免循环的要求。 - PhiLho
26
不是不喜欢使用循环,只是在寻找一个惯用语的一行代码。 - Bart
2
循环语句就是为了解决这样的问题而设计的,在通用工具类中编写循环,然后调用你刚创建的一行代码。 - che javara
字符串相关的类似问题:https://dev59.com/_HRA5IYBdhLWcg3w9ivq - koppor
显示剩余2条评论
48个回答

16

受Yonatan解决方案的启发,这里有一个完全基于递归的解决方案 - 仅使用了length()charAt()两种库方法,它们都没有进行任何循环:

public static int countOccurrences(String haystack, char needle)
{
    return countOccurrences(haystack, needle, 0);
}

private static int countOccurrences(String haystack, char needle, int index)
{
    if (index >= haystack.length())
    {
        return 0;
    }

    int contribution = haystack.charAt(index) == needle ? 1 : 0;
    return contribution + countOccurrences(haystack, needle, index+1);
}

递归是否算作循环取决于你使用的确切定义,但这可能是你能得到的最接近循环的东西。

我不知道大多数JVM现在是否支持尾递归... 如果不支持,则对于足够长的字符串,您将遇到同名的堆栈溢出问题。


不,尾递归可能会在Java 7中出现,但它还没有普及。这种简单、直接的尾递归可以在编译时转换为循环,但Java 7的东西实际上是内置到JVM中处理通过不同方法的链接。 - erickson
3
如果你的方法返回一个对自身的调用(包括一个运行总和参数),而不是返回执行加法的结果,那么你更有可能得到尾递归。 - Stephen Denne

16

我不喜欢为此目的分配新字符串的想法。由于字符串已经在后端有一个存储其值的字符数组,所以String.charAt()实际上是免费的。

for(int i=0;i<s.length();num+=(s.charAt(i++)==delim?1:0))

在不需要进行额外内存分配和垃圾回收的情况下,只需使用 J2SE 中的 1 行或更少代码即可完成操作。


要对这个表达式多点关注,因为它是唯一一次在字符串上进行单次遍历。我很在意性能。 - WestCoastProjects
2
charAt 迭代 16 位代码点而不是字符!在 Java 中,char 不是一个字符。因此,这个答案意味着没有 Unicode 符号的高代理等于 delim 的代码点。我不确定对于点是否正确,但一般来说可能不正确。 - ceving

12
受 Jon Skeet 启发,这是一个非循环版本,不会使您的堆栈溢出。如果您想使用 fork-join 框架,这也是一个有用的起点。
public static int countOccurrences(CharSequeunce haystack, char needle) {
    return countOccurrences(haystack, needle, 0, haystack.length);
}

// Alternatively String.substring/subsequence use to be relatively efficient
//   on most Java library implementations, but isn't any more [2013].
private static int countOccurrences(
    CharSequence haystack, char needle, int start, int end
) {
    if (start == end) {
        return 0;
    } else if (start+1 == end) {
        return haystack.charAt(start) == needle ? 1 : 0;
    } else {
        int mid = (end+start)>>>1; // Watch for integer overflow...
        return
            countOccurrences(haystack, needle, start, mid) +
            countOccurrences(haystack, needle, mid, end);
    }
}

(免责声明:未经测试,未编译,不合理。)

也许这是最好的(单线程,没有代理对支持)编写它的方法:

public static int countOccurrences(String haystack, char needle) {
    int count = 0;
    for (char c : haystack.toCharArray()) {
        if (c == needle) {
           ++count;
        }
    }
    return count;
}

12

我不确定这个方法的效率如何,但这是我在不使用第三方库的情况下能够编写的最短代码:

public static int numberOf(String target, String content)
{
    return (content.split(target).length - 1);
}

5
为了在字符串末尾也计数出现次数,您需要使用负限制参数调用split()方法,像这样:return (content.split(target, -1).length - 1);。默认情况下,在split()方法返回的数组中,末尾处的出现次数会被省略掉。请参考Doku - vlz

11

使用,你也可以使用流来实现这一点。显然,在幕后有一个迭代,但你不必明确地编写它!

public static long countOccurences(String s, char c){
    return s.chars().filter(ch -> ch == c).count();
}

countOccurences("a.b.c.d", '.'); //3
countOccurences("hello world", 'l'); //3

使用.codePoints()而不是.chars()将支持任何Unicode值(包括那些需要代理对的值)。 - Luke Usherwood

10

在Java 8中,也可以使用reduce来解决这个问题:

int res = "abdsd3$asda$asasdd$sadas".chars().reduce(0, (a, c) -> a + (c == '$' ? 1 : 0));
System.out.println(res);

输出:

3

9
以下是获取答案的最简单方法:

只需按照以下步骤操作:

public static void main(String[] args) {
    String string = "a.b.c.d";
    String []splitArray = string.split("\\.",-1);
    System.out.println("No of . chars is : " + (splitArray.length-1));
}

3
这段代码不能正确地返回给定输入"a.b.c."中点的数量。 - dekaru
@dekaru,您可以把您的字符串粘贴在评论中,这样我们就可以看一下了。 - Amar Magar

8

完整示例:

public class CharacterCounter
{

  public static int countOccurrences(String find, String string)
  {
    int count = 0;
    int indexOf = 0;

    while (indexOf > -1)
    {
      indexOf = string.indexOf(find, indexOf + 1);
      if (indexOf > -1)
        count++;
    }

    return count;
  }
}

电话:

int occurrences = CharacterCounter.countOccurrences("l", "Hello World.");
System.out.println(occurrences); // 3

错误的代码,当我尝试使用 int occurrences = CharacterCounter.countOccurrences("1", "101"); 时它无法工作。 System.out.println(occurrences); // 1 - jayesh
我提交了一个修复代码的版本,它使用相同的逻辑。 - MaanooAk

5

如果您正在使用Spring框架,您也可以使用“StringUtils”类。方法将是“countOccurrencesOf”。


5

一种更简单的解决方案是根据匹配字符来拆分字符串。

例如,

int getOccurences(String characters, String string) { String[] words = string.split(characters); return words.length - 1; }

在以下情况下将返回4: getOccurences("o", "something about a quick brown fox");


问题在于需要分配一个数组,这会非常慢。 - Palec

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