75

Is there a simple way (instead of traversing manually all the string, or loop for indexOf) in order to find how many times, a character appears in a string?

Say we have "abdsd3$asda$asasdd$sadas" and we want that $ appears 3 times.

Dennis Meng
  • 5,109
  • 14
  • 33
  • 36
George Kastrinis
  • 4,924
  • 4
  • 29
  • 46

15 Answers15

125
public int countChar(String str, char c)
{
    int count = 0;

    for(int i=0; i < str.length(); i++)
    {    if(str.charAt(i) == c)
            count++;
    }

    return count;
}

This is definitely the fastest way. Regexes are much much slower here, and possible harder to understand.

Daniel
  • 27,718
  • 20
  • 89
  • 133
  • 1
    Ooh, don't you mind using enhanced for loop instead of C-style going through indices? – Dmitry Ginzburg May 28 '14 at 08:48
  • I don't want to use implicit iterators. – Daniel Jun 02 '14 at 08:38
  • 2
    firstly, surely, there's no shame to use iterators, what's the problem? Secondly, iterators are not used for looping arrays, see, for example, http://stackoverflow.com/a/7956673/1828937. – Dmitry Ginzburg Jun 02 '14 at 10:13
  • 1
    @DmitryGinzburg: The problem is we don't have a char array here, but a String. The alternate way would be to iterate over s.toCharArray(), but this would involve a copy of the Strings internal char array. – Daniel Jun 21 '14 at 06:03
  • 1
    But, what if we have to find each character's occurrence in the string? – Kaveesh Kanwal Mar 23 '15 at 19:26
69

Functional style (Java 8, just for fun):

str.chars().filter(num -> num == '$').count()
Dmitry Ginzburg
  • 7,391
  • 2
  • 37
  • 48
32

Not optimal, but simple way to count occurrences:

String s = "...";
int counter = s.split("\\$", -1).length - 1;

Note:

  • Dollar sign is a special Regular Expression symbol, so it must be escaped with a backslash.
  • A backslash is a special symbol for escape characters such as newlines, so it must be escaped with a backslash.
  • The second argument of split prevents empty trailing strings from being removed.
Yeti
  • 2,647
  • 2
  • 33
  • 37
Marcelo
  • 11,218
  • 1
  • 37
  • 51
  • 6
    Almost a good idea. This fails on some simple cases though. e.g. s = "$" – noddy May 03 '14 at 01:47
  • Doesn't work because $ is a regex metacharacter. – Boann Nov 16 '14 at 13:42
  • 1
    I corrected the meta character issue in the answer. – Marcelo Jan 13 '15 at 20:22
  • That still doesn't work with trailing "$"s, e.g. "abdsd3$asda$asasdd$sadas$" , because empty trailing items are ignored by default. It should be `int counter = s.split("\$", -1).length - 1;` – Hirofumi Okino Jan 05 '16 at 11:45
  • `"\$"` should be replaced with `"\\$"`, because the backslash already has a special meaning that is unique to the string itself, such as newlines, tabs, etc. (escape character) Therefore a double backslash is needed. – Yeti Jun 01 '16 at 19:38
  • it doesn't work in case of last char searching ex:"356595".split("5") -> size =3 "356559".split("5") -> size = 4 – oyenigun Oct 12 '17 at 06:22
25

You can use Apache Commons' StringUtils.countMatches(String string, String subStringToCount).

thersch
  • 1,316
  • 12
  • 19
7

Since you're scanning the whole string anyway you can build a full character count and do any number of lookups, all for the same big-Oh cost (n):

public static Map<Character,Integer> getCharFreq(String s) {
  Map<Character,Integer> charFreq = new HashMap<Character,Integer>();
  if (s != null) {
    for (Character c : s.toCharArray()) {
      Integer count = charFreq.get(c);
      int newCount = (count==null ? 1 : count+1);
      charFreq.put(c, newCount);
    }
  }
  return charFreq;
}

// ...
String s = "abdsd3$asda$asasdd$sadas";
Map counts = getCharFreq(s);
counts.get('$'); // => 3
counts.get('a'); // => 7
counts.get('s'); // => 6
maerics
  • 151,642
  • 46
  • 269
  • 291
5

A character frequency count is a common task for some applications (such as education) but not general enough to warrant inclusion with the core Java APIs. As such, you'll probably need to write your own function.

maerics
  • 151,642
  • 46
  • 269
  • 291
4

I believe the "one liner" that you expected to get is this:

"abdsd3$asda$asasdd$sadas".replaceAll( "[^$]*($)?", "$1" ).length();

Remember that the requirements are:

(instead of traversing manually all the string, or loop for indexOf)

and let me add: that at the heart of this question it sounds like "any loop" is not wanted and there is no requirement for speed. I believe the subtext of this question is coolness factor.

4

you can also use a for each loop. I think it is simpler to read.

int occurrences = 0;
for(char c : yourString.toCharArray()){
   if(c == '$'){
      occurrences++;
   }
}
ironmaurus
  • 918
  • 8
  • 8
4

Something a bit more functional, without Regex:

public static int count(String s, char c) {
    return s.length()==0 ? 0 : (s.charAt(0)==c ? 1 : 0) + count(s.substring(1),c);
}

It's no tail recursive, for the sake of clarity.

srcarro
  • 61
  • 3
3

Traversing the string is probably the most efficient, though using Regex to do this might yield cleaner looking code (though you can always hide your traverse code in a function).

tofutim
  • 22,664
  • 20
  • 87
  • 148
  • 2
    Regex = cleaner? Maybe shorter, but I would say regexes are generally pretty cryptic (and less _clean_) when compared to basic looping code. – jahroy May 30 '13 at 04:52
3

Well there are a bunch of different utilities for this, e.g. Apache Commons Lang String Utils

but in the end, it has to loop over the string to count the occurrences one way or another.

Note also that the countMatches method above has the following signature so will work for substrings as well.

public static int countMatches(String str, String sub)

The source for this is (from here):

public static int countMatches(String str, String sub) {
    if (isEmpty(str) || isEmpty(sub)) {
        return 0;
    }
    int count = 0;
    int idx = 0;
    while ((idx = str.indexOf(sub, idx)) != -1) {
        count++;
        idx += sub.length();
    }
    return count;
}

I was curious if they were iterating over the string or using Regex.

Casey
  • 12,070
  • 18
  • 71
  • 107
2

This is simple code, but of course a little bit slower.

String s = ...;
int countDollar = s.length()-s.replaceAll("\\$","").length();
int counta = s.length()-s.replaceAll("a","").length();

An even better answer is here in a duplicate question

Community
  • 1
  • 1
0

There is another way to count the number of characters in each string. Assuming we have a String as String str = "abfdvdvdfv"

We can then count the number of times each character appears by traversing only once as

for (int i = 0; i < str.length(); i++) 
{
    if(null==map.get(str.charAt(i)+""))
    {
        map.put(str.charAt(i)+"", new Integer(1));
    }
    else
    {
       Integer count = map.get(str.charAt(i)+"");
       map.put(str.charAt(i)+"", count+1);
    }
}

We can then check the output by traversing the Map as

for (Map.Entry<String, Integer> entry:map.entrySet()) 
{
    System.out.println(entry.getKey()+" count is : "+entry.getValue())

}
Lonergan6275
  • 1,938
  • 6
  • 32
  • 63
PipoTells
  • 511
  • 6
  • 11
  • This is needlessly complex, you can just use an array (256 long if only only basic characters are used, or 65535 long for the entire range java supports). do something like `array[str.charAt(i)]++` instead of that if/else branch, and then on the output, just do `for(int i = 0; i < length; ++i) if(array[i] != 0) System.out.println((char)i + " = " + array[i])`. – Chase Nov 29 '14 at 14:24
0

You can look at sorting the string -- treat it as a char array -- and then do a modified binary search which counts occurrences? But I agree with @tofutim that traversing it is the most efficient -- O(N) versus O(N * logN) + O(logN)

Liv
  • 6,006
  • 1
  • 22
  • 29
-1
 public static int countChars(String input,char find){      
            if(input.indexOf(find) != -1){          
            return  countChars(input.substring(0, input.indexOf(find)), find)+ 
                countChars(input.substring(input.indexOf(find)+1),find) + 1;
            }
            else {
                return 0;
            }

        }
  • 1
    This works, but it's confusing and overcomplicated. Why use recursion - let alone this convoluted implementation - when iteration can be used simply and cleanly? Also, `indexOf`finds the leftmost index of the target string, so `countChars(input.substring(0, input.indexOf(find)), find)` will always be equal to zero; you could scrap the first line of your return expression and get the same result. – Mark Amery Jan 26 '13 at 16:03