【问题标题】:How do I count the number of occurrences of a char in a String?如何计算字符串中字符的出现次数?
【发布时间】:2010-09-21 12:22:02
【问题描述】:

我有字符串

a.b.c.d

我想计算 '.' 的出现次数以惯用的方式,最好是单行。

(之前我将此约束表达为“没有循环”,以防你想知道为什么每个人都试图在不使用循环的情况下回答)。

【问题讨论】:

  • 家庭作业?因为否则我看不到避免循环的要求。
  • 比起寻找惯用的单线,不反对循环。
  • 循环是为这样的问题而设计的,在一个通用的实用程序类中编写循环,然后调用你新创建的一个班轮。
  • 字符串的类似问题:stackoverflow.com/questions/767759/…
  • 只是指出——我很高兴找到单行,它很有趣并且(作为一个真正的优势)通常很容易记住,但我想指出一个单独的方法和一个循环在几乎所有方面都更好——可读性甚至性能。下面的大多数“优雅”解决方案都不会很好地执行,因为它们涉及重组字符串/复制内存,而仅扫描字符串并计算出现次数的循环会快速而简单。并不是说性能通常应该是一个因素,但不要在循环中查看单行并假设它会表现更好。

标签: java string


【解决方案1】:

这个怎么样。它不使用下面的正则表达式,因此应该比其他一些解决方案更快,并且不会使用循环。

int count = line.length() - line.replace(".", "").length();

【讨论】:

  • 最简单的方法。聪明的一个。它适用于没有 StringUtils 类的 Android
  • 这是最好的答案。它是最好的原因是因为您不必导入另一个库。
  • 非常实用但丑得要命。我不推荐它,因为它会导致代码混乱。
  • 丑陋的代码可以通过在你自己的“StringUtils”类中作为一个方法来最小化。那么丑陋的代码就在一个地方,其他地方都可读性很好。
  • 循环方法比这快很多。特别是当想要计算一个字符而不是一个字符串时(因为没有 String.replace(char, char) 方法)。在 15 个字符的字符串上,我得到 6049 ns 与 26,739 ns 的差异(平均超过 100 次运行)。原始数字差异很大,但从百分比来看……它加起来了。避免内存分配 - 使用循环!
【解决方案2】:

我的“惯用单行”是:

int count = StringUtils.countMatches("a.b.c.d", ".");

既然已经在commons lang,为什么还要自己写呢?

Spring 框架的单线器是:

int occurance = StringUtils.countOccurrencesOf("a.b.c.d", ".");

【讨论】:

  • Guava 等价物:int count = CharMatcher.is('.').countIn("a.b.c.d"); ...作为重复问题中的answered by dogbane
  • 虽然我不会对此投反对票,但它是 (a) 需要 3rd 方库和 (b) 昂贵。
  • 这只适用于弹簧框架必须导入。
  • 在我工作过的每家公司中,代价高昂的是编写和维护不善的“*Utils”类。您的部分工作是了解 Apache Commons 中可用的内容。
【解决方案3】:

总结其他答案以及我所知道的使用单行符的所有方法:

   String testString = "a.b.c.d";

1) 使用 Apache Commons

int apache = StringUtils.countMatches(testString, ".");
System.out.println("apache = " + apache);

2) 使用 Spring 框架的

int spring = org.springframework.util.StringUtils.countOccurrencesOf(testString, ".");
System.out.println("spring = " + spring);

3) 使用替换

int replace = testString.length() - testString.replace(".", "").length();
System.out.println("replace = " + replace);

4) 使用 replaceAll(案例 1)

int replaceAll = testString.replaceAll("[^.]", "").length();
System.out.println("replaceAll = " + replaceAll);

5) 使用 replaceAll(案例 2)

int replaceAllCase2 = testString.length() - testString.replaceAll("\\.", "").length();
System.out.println("replaceAll (second case) = " + replaceAllCase2);

6) 使用拆分

int split = testString.split("\\.",-1).length-1;
System.out.println("split = " + split);

7) 使用Java8(案例1)

long java8 = testString.chars().filter(ch -> ch =='.').count();
System.out.println("java8 = " + java8);

8) 使用 Java8(案例 2),对于 unicode 可能比案例 1 更好

long java8Case2 = testString.codePoints().filter(ch -> ch =='.').count();
System.out.println("java8 (second case) = " + java8Case2);

9) 使用 StringTokenizer

int stringTokenizer = new StringTokenizer(" " +testString + " ", ".").countTokens()-1;
System.out.println("stringTokenizer = " + stringTokenizer);

来自评论:小心 StringTokenizer,对于 abcd 它会起作用,但对于 a...bc...d 或 ...abcd 或 a....b... ...c.....d... 或等它不会起作用。它只是算数。字符之间只有一次

更多信息github

Perfomance test(使用JMH,模式=AverageTime,得分0.010优于0.351):

Benchmark              Mode  Cnt  Score    Error  Units
1. countMatches        avgt    5  0.010 ±  0.001  us/op
2. countOccurrencesOf  avgt    5  0.010 ±  0.001  us/op
3. stringTokenizer     avgt    5  0.028 ±  0.002  us/op
4. java8_1             avgt    5  0.077 ±  0.005  us/op
5. java8_2             avgt    5  0.078 ±  0.003  us/op
6. split               avgt    5  0.137 ±  0.009  us/op
7. replaceAll_2        avgt    5  0.302 ±  0.047  us/op
8. replace             avgt    5  0.303 ±  0.034  us/op
9. replaceAll_1        avgt    5  0.351 ±  0.045  us/op

【讨论】:

【解决方案4】:

迟早,某事必须循环。编写(非常简单的)循环比使用像 split 这样的东西要简单得多,它比你需要的要强大得多。

无论如何都要将循环封装在一个单独的方法中,例如

public static int countOccurrences(String haystack, char needle)
{
    int count = 0;
    for (int i=0; i < haystack.length(); i++)
    {
        if (haystack.charAt(i) == needle)
        {
             count++;
        }
    }
    return count;
}

那么你不需要在你的主代码中有循环 - 但循环必须在某个地方。

【讨论】:

  • for (int i=0,l=haystack.length(); i
  • (我什至不确定评论的“堆栈”位来自哪里。这不像 this 答案是我的递归答案,这确实令人讨厌堆栈。)
  • 不仅如此,这可能是一种反优化,而无需查看 jit 的作用。例如,如果您在数组 for 循环上执行上述操作,您可能会使事情变得更糟。
  • @sulai:Chris 的担忧是毫无根据的,IMO,面对微不足道的 JIT 优化。三年多之后,这个评论现在引起你的注意有什么原因吗?只是感兴趣。
  • 可能@sulai 只是像我一样遇到了这个问题(同时想知道 Java 是否为此提供了内置方法)并且没有注意到日期。但是,我很好奇将length() 调用移到循环之外会如何使性能更差,正如@ShuggyCoUk 提到的那样,提高了几个cmets。
【解决方案5】:

我有一个和姆拉登类似的想法,但恰恰相反……

String s = "a.b.c.d";
int charCount = s.replaceAll("[^.]", "").length();
println(charCount);

【讨论】:

  • 正确。 ReplaceAll(".") 将替换任何字符,而不仅仅是点。 ReplaceAll("\\.") 会起作用的。您的解决方案更直接。
  • jjnguy 在看到我的 "a.b.c.d".split("\\.").length-1 解决方案后实际上首先建议了 replaceAll("[^.]") 。但在被击中 5 次后,我删除了我的答案(和他的评论)。
  • "...现在你有两个问题"(必须的。)无论如何,我敢打赌在 replaceAll()length() 中执行了数十个循环。好吧,如果它不可见,它就不存在;o)
  • 我认为使用正则表达式并为计数创建一个新字符串不是一个好主意。我会创建一个静态方法,循环字符串中的每个字符来计算数字。
  • @mingfai:确实,但最初的问题是关于制作单行,甚至没有循环(你可以在一行中做一个循环,但这会很丑!)。质疑问题,而不是答案... :-)
【解决方案6】:
String s = "a.b.c.d";
int charCount = s.length() - s.replaceAll("\\.", "").length();

ReplaceAll(".") 将替换所有字符。

PhiLho's solution 使用 ReplaceAll("[^.]",""),不需要转义,因为 [.] 表示字符“点”,而不是“任何字符”。

【讨论】:

  • 我喜欢这个。当然,那里仍然有一个循环,因为必须有。
  • 请注意,如果您想查找长度 > 1 的子字符串,您需要将此数字相除
【解决方案7】:

我的“惯用单行”解决方案:

int count = "a.b.c.d".length() - "a.b.c.d".replace(".", "").length();

不知道为什么接受使用 StringUtils 的解决方案。

【讨论】:

  • 这篇文章中有一个类似的旧解决方案。
  • 因为这个方案实在是太低效了
  • 这会创建一个额外的字符串来产生计数。如果 StringUtils 是一个选项,不知道为什么有人会喜欢它而不是 StringUtils。如果这不是一个选项,他们应该只在实用程序类中创建一个简单的 for 循环。
【解决方案8】:
String s = "a.b.c.d";
long result = s.chars().filter(ch -> ch == '.').count();

【讨论】:

  • 投票 + 支持原生解决方案。
【解决方案9】:

一个简短的例子是

String text = "a.b.c.d";
int count = text.split("\\.",-1).length-1;

【讨论】:

  • 这似乎有一个比较大的开销,请注意它可能会创建很多小字符串。通常这并不重要,但要小心使用。
【解决方案10】:

这是一个没有循环的解决方案:

public static int countOccurrences(String haystack, char needle, int i){
    return ((i=haystack.indexOf(needle, i)) == -1)?0:1+countOccurrences(haystack, needle, i+1);}


System.out.println("num of dots is "+countOccurrences("a.b.c.d",'.',0));

嗯,有一个循环,但它是不可见 :-)

-- 约纳坦

【讨论】:

  • 除非你的字符串太长你得到一个 OutOfMemoryError。
  • 这个问题听起来很做作,足以成为家庭作业,如果是这样,这个递归可能就是你被要求找到的答案。
  • 这使用 indexOf,它将循环......但一个好主意。在一分钟内发布一个真正“只是递归”的解决方案......
  • 如果出现的次数超过了可用的堆栈槽位,则会出现堆栈溢出异常;)
  • 循环并不危险。不受控制的深度递归真的很危险。
【解决方案11】:

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

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

只用 J2SE 就可以做到这一点,无需额外的需要收集的分配,在 1 行或更短的时间内完成。

【讨论】:

  • 对这个给予一些爱,因为它是唯一一个对字符串进行单次传递的人。我确实关心性能。
  • charAt 迭代 16 位代码点而不是字符! Java 中的 char 不是字符。所以这个答案意味着必须没有 Unicode 符号的高代理项等于delim 的代码点。我不确定点是否正确,但总的来说可能不正确。
【解决方案12】:

好的,受 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 内置的,可以通过不同的方法处理链接。
  • 如果您的方法返回对自身的调用(包括运行总参数),而不是返回执行加法的结果,您更有可能获得尾递归。
【解决方案13】:

受 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;
}

【讨论】:

    【解决方案14】:

    不确定它的效率,但这是我在不引入 3rd 方库的情况下可以编写的最短代码:

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

    【讨论】:

    • 要计算字符串末尾的出现次数,您必须使用负限制参数调用 split,如下所示:return (content.split(target, -1).length - 1);。默认情况下,字符串末尾的出现在 split() 生成的数组中被省略。见Doku
    【解决方案15】:

    使用,您还可以使用流来实现此目的。明明背后有一个迭代,但你不必显式写出来!

    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 值(包括那些需要代理对的值)
    【解决方案16】:

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

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

    输出:

    3
    

    【讨论】:

      【解决方案17】:

      得到答案的最简单方法如下:

      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));
      }
      

      【讨论】:

      • 对于给定的输入“a.b.c.”,此 sn-p 不会返回正确的点数
      • @dekaru 能否请您在评论中粘贴您的刺痛,以便我们查看。
      【解决方案18】:

      完整示例:

      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(出现次数); // 1
      • 我对使用相同逻辑的代码进行了修复
      【解决方案19】:

      如果您使用的是 Spring 框架,您还可以使用“StringUtils”类。 该方法将是“countOccurrencesOf”。

      【讨论】:

        【解决方案20】:

        只需一行代码即可使用split()函数

        int noOccurence=string.split("#",-1).length-1;
        

        【讨论】:

        • Split 确实创建了字符串数组,很耗时。
        • 你说得对,这是一个真正的问题。以另一种方式,它避免在您的项目中引入第三方库(如果尚未完成)。这取决于您想做什么以及性能期望是什么。
        • 此解决方案将不包括尾随的空命中,因为在此重载拆分方法调用中参数limit 设置为零。一个例子:"1##2#3#####".split("#") 只会产生一个大小为 4 ([0:"1";1:""; 2:"2"; 3:"3"]) 的数组,而不是大小为 9 ([0:"1"; 1:""; 2:"2"; 3:"3"; 4:""; 5:""; 6:""; 7:""; 8:""]) 的数组。
        【解决方案21】:

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

        例如,

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

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

        【讨论】:

        • 这里的问题是必须分配一个数组,这非常慢。
        【解决方案22】:
        public static int countOccurrences(String container, String content){
            int lastIndex, currIndex = 0, occurrences = 0;
            while(true) {
                lastIndex = container.indexOf(content, currIndex);
                if(lastIndex == -1) {
                    break;
                }
                currIndex = lastIndex + content.length();
                occurrences++;
            }
            return occurrences;
        }
        

        【讨论】:

          【解决方案23】:

          虽然方法可以隐藏它,但没有循环(或递归)就无法计数。不过,出于性能原因,您想使用 char[]。

          public static int count( final String s, final char c ) {
            final char[] chars = s.toCharArray();
            int count = 0;
            for(int i=0; i<chars.length; i++) {
              if (chars[i] == c) {
                count++;
              }
            }
            return count;
          }
          

          使用 replaceAll(即 RE)听起来不是最好的方法。

          【讨论】:

          • 我认为这是最优雅的解决方案。为什么你使用 toCharArray 而不是直接使用 charAt?
          • 用 charAt 循环至少以前比较慢。也可能取决于平台。真正找出答案的唯一方法是衡量差异。
          【解决方案24】:
          import java.util.Scanner;
          
          class apples {
          
              public static void main(String args[]) {    
                  Scanner bucky = new Scanner(System.in);
                  String hello = bucky.nextLine();
                  int charCount = hello.length() - hello.replaceAll("e", "").length();
                  System.out.println(charCount);
              }
          }//      COUNTS NUMBER OF "e" CHAR´s within any string input
          

          【讨论】:

            【解决方案25】:

            好吧,我偶然发现了这个线程。 我没有看到任何编程语言限制,因为 groovy 在 java vm 上运行: 以下是我使用 Groovy 解决问题的方法。

            "a.b.c.".count(".")
            

            完成。

            【讨论】:

              【解决方案26】:

              使用Eclipse Collections

              int count = Strings.asChars("a.b.c.d").count(c -> c == '.');
              

              如果要计算多个字符,可以使用CharBag,如下所示:

              CharBag bag = Strings.asChars("a.b.c.d").toBag();
              int count = bag.occurrencesOf('.');
              

              注意:我是 Eclipse Collections 的提交者。

              【讨论】:

                【解决方案27】:

                在代码的某个地方,有些东西必须循环。解决这个问题的唯一方法是完全展开循环:

                int numDots = 0;
                if (s.charAt(0) == '.') {
                    numDots++;
                }
                
                if (s.charAt(1) == '.') {
                    numDots++;
                }
                
                
                if (s.charAt(2) == '.') {
                    numDots++;
                }
                

                ...等等,但是您是在源代码编辑器中手动执行循环的人 - 而不是将运行它的计算机。见伪代码:

                create a project
                position = 0
                while (not end of string) {
                    write check for character at position "position" (see above)
                }
                write code to output variable "numDots"
                compile program
                hand in homework
                do not think of the loop that your "if"s may have been optimized and compiled to
                

                【讨论】:

                  【解决方案28】:

                  这里是一个风格略有不同的递归解决方案:

                  public static int countOccurrences(String haystack, char needle)
                  {
                      return countOccurrences(haystack, needle, 0);
                  }
                  
                  private static int countOccurrences(String haystack, char needle, int accumulator)
                  {
                      if (haystack.length() == 0) return accumulator;
                      return countOccurrences(haystack.substring(1), needle, haystack.charAt(0) == needle ? accumulator + 1 : accumulator);
                  }
                  

                  【讨论】:

                    【解决方案29】:

                    为什么不直接分割字符然后得到结果数组的长度。数组长度总是实例数 + 1。对吗?

                    【讨论】:

                      【解决方案30】:

                      以下源代码将为您提供用户输入的单词中给定字符串的出现次数:-

                      import java.util.Scanner;
                      
                      public class CountingOccurences {
                      
                          public static void main(String[] args) {
                      
                              Scanner inp= new Scanner(System.in);
                              String str;
                              char ch;
                              int count=0;
                      
                              System.out.println("Enter the string:");
                              str=inp.nextLine();
                      
                              while(str.length()>0)
                              {
                                  ch=str.charAt(0);
                                  int i=0;
                      
                                  while(str.charAt(i)==ch)
                                  {
                                      count =count+i;
                                      i++;
                                  }
                      
                                  str.substring(count);
                                  System.out.println(ch);
                                  System.out.println(count);
                              }
                      
                          }
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 2012-10-03
                        • 2014-04-24
                        • 2020-02-21
                        • 2012-02-12
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多