views:

12256

answers:

14

I have the string

a.b.c.d

I want to count the occurrences of '.' in an idiomatic way, preferably a one-liner.

(Previously I had expressed this constraint as "without a loop", in case you're wondering why everyone's trying to answer without using a loop).

+2  A: 

Seems a shame to allocate an array just to count the occurrences of a character. Why not write the loop?

Ned Batchelder
Yes. Someone has to write it, why not you?
tvanfosson
Indeed. If you don't want to look at it, then write it as a function off in a library somewhere.
GeekyMonkey
+17  A: 

Sooner or later, something has to loop. It's far simpler for you to write the (very simple) loop than to use something like split which is much more powerful than you need.

By all means encapsulate the loop in a separate method, e.g.

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

Then you don't need have the loop in your main code - but the loop has to be there somewhere.

Jon Skeet
Sounds like a homework question to me. Still, +1 for being the voice of reason.
Bill the Lizard
for (int i=0,l=haystack.length(); i < l; i++) be kind to your stack
Chris
@Chris, are you one working on a PC from the 80-ies that you're worried about such things?
Bart Kiers
i see, "Jon" can do that ... ok
Chris
@Chris: that is utter non-sense. Anyone not working on a PC from 1980 can do that.
Bart Kiers
And indeed any Java JIT compiler worth its salt will be able to perform the relevant optimisation. Micro-optimising at the expense of readability is not a good idea.
Jon Skeet
(I'm not even sure where the "stack" bit of the comment comes from. It's not like *this* answer is my recursive one, which is indeed nasty to the stack.)
Jon Skeet
not only that but this is possibly an anti optimization without taking a look at what the jit does. If you did the above on an array for loop for example you might make things worse.
ShuggyCoUk
+8  A: 

here is a solution without a loop:

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

well, there is a loop, but it is invisible :-)

-- Yonatan

Yonatan Maman
Unless your string is so long you get an OutOfMemoryError.
Spencer K
The problem sounds contrived enough to be homework, and if so, this recursion is probably the answer you're being asked to find.
erickson
That uses indexOf, which will loop... but a nice idea. Posting a truly "just recursive" solution in a minute...
Jon Skeet
+11  A: 
String s = "a.b.c.d";
int charCount = s.length() - s.replaceAll("\\.", "").length();

ReplaceAll(".") would replace all characters.

PhiLho's solution uses ReplaceAll("[^.]",""), which does not need to be escaped, since [.] represents the character 'dot', not 'any character'.

Mladen Prajdic
I like this one.
jjnguy
I like this one. There's still a loop there, of course, as there has to be.
Paul
This one is easy to understand.
Ben Page
Untested, eh? :-)
PhiLho
well i wrote it in c# :) i'm not a java guy. but it's the principle that counts right? :))
Mladen Prajdic
PhiLho's solution is better
VonC
+18  A: 

I had an idea similar to Mladen, but the opposite...

String s = "a.b.c.d";
int charCount = s.replaceAll("[^.]", "").length();
println(charCount);
PhiLho
Correct. ReplaceAll(".") would replace any character, not just dot. ReplaceAll("\\.") would have worked. Your solution is more straightforward.
VonC
jjnguy had actually suggested a replaceAll("[^.]") first, upon seeing my "a.b.c.d".split("\\.").length-1 solution. But after being hit 5 times, I deleted my answer (and his comment).
VonC
"...now you have two problems" (oblig.) Anyway, I'd bet that there are tens of loops executing in `replaceAll()` and `length()`. Well, if it's not visible, it doesn't exist ;o)
Piskvor
+3  A: 

Okay, inspired my Monatan's solution, here's one which is purely recursive - the only library methods used are length() and charAt(), neither of which do any looping:

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

Whether recursion counts as looping depends on which exact definition you use, but it's probably as close as you'll get.

I don't know whether most JVMs do tail-recursion these days... if not you'll get the eponymous stack overflow for suitably long strings, of course.

Jon Skeet
No, tail recursion will probably be in Java 7, but it's not widespread yet. This simple, direct tail recursion could be translated to a loop at compile time, but the Java 7 stuff is actually built-in to the JVM to handle chaining through different methods.
erickson
You'd be more likely to get tail recursion if your method returned a call to itself (including a running total parameter), rather than returning the result of performing an addition.
Stephen Denne
A: 

While methods can hide it, there is no way to count without a loop (or recursion). You want to use a char[] for performance reasons though.

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

Using replaceAll (that is RE) does not sound like the best way to go.

tcurdt
+1  A: 

Inspired by Jon Skeet, a non-loop version that wont blow your stack. Also useful starting point if you want to use the fork-join framework.

public static int countOccurrences(CharSequeunce haystack, char needle) {
    return countOccurrences(haystack, needle, 0, haystack.length);
}

// Alternatively String.substring/subsequence is relatively efficient
//   on most, but not all, Java library implementations.
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);
    }
}

(Disclaimer: Not tested, not compiled, not sensible.)

Perhaps the best (single-threaded, no surrogate-pair support) way to write it:

public static int countOccurrences(CharSequeunce haystack, char needle) {
    int count = 0;
    for (char c : haystack.toCharArray()) {
        if (c == needle) {
           ++count;
        }
    }
    return count;
}
Tom Hawtin - tackline
A: 

Somewhere in the code, something has to loop. The only way around this is a complete unrolling of the loop:

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

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


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

...etc, but then you're the one doing the loop, manually, in the source editor - instead of the computer that will run it. See the pseudocode:

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
Piskvor
A: 

Here is a slightly different style recursion solution:

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);
}
Stephen Denne
A: 

omg java api is from the past. when will we have this very basic feature on the most popular programming language in the world? millions of classes, billions of methods, but there is no a simple plain way to count substring ocurrences.

This functionality is only useful for answering assignments IMHO. Java isn't the coolest language in the world, but it is designed to have useful functionality for real world programs.
Peter Lawrey
Not sure how complaining about a language you don't like really adds a lot to the discussion
Matt
A: 

A shorter example is

String text = "a.b.c.d";
int count = text.split("\\.",-1).length-1;
Peter Lawrey
+10  A: 

My 'idiomatic one-liner' for this is:

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

Why write it yourself when it's already in commons lang?

Cowan
A: 

StringTokenizer stOR = new StringTokenizer(someExpression, "||"); int orCount = stOR.countTokens()-1;

BeeCreative