Как подсчитать количество вхождений символа в строку?

У меня есть строка

a.b.c.d

Я хочу подсчитать вхождения '.- в идиоматическом смысле, предпочтительно однострочный.

(ранее я выражал это ограничение как "без цикла", Если вам интересно, почему все пытаются ответить без использования цикла).

30 ответов


мой "идиоматический однострочный" для этого:

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

зачем писать его самостоятельно, когда он уже в commons lang?

Spring Framework oneliner для этого:

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

Как насчет этого. Он не использует regexp внизу, поэтому должен быть быстрее, чем некоторые другие решения, и не будет использовать цикл.

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

суммировать другой ответ и то, что я знаю все способы сделать это с помощью однострочного:

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

1) с помощью Apache Commons

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

2) с помощью весенние рамки

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, для a.b.c.d это будет работать, но для a...б.С....D или ...а.б.С. Д или....си......с.....д... или etc. это не сработает. Это просто будет считаться . между персонажами только один раз

дополнительная информация в github

тест производительности (через JMH, mode = среднее время, оценка 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

рано или поздно что-то для петли. Гораздо проще написать (очень простой) цикл, чем использовать что-то вроде 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;
}

тогда вам не нужно иметь петли в основной код, но цикл должен быть где-то там.


У меня была идея, похожая на Младена, но противоположная...

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

String s = "a.b.c.d";
int charCount = s.length() - s.replaceAll("\.", "").length();

ReplaceAll(".") заменит все символы.

PhiLho это!--5--> использует ReplaceAll (" [^.] ", ""), который не нужно избегать, так как [.] представляет символ "точка", а не "любой символ".


мое "идиоматическое однострочное" решение:

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

понятия не имею, почему принимается решение, использующее StringUtils.


String s = "a.b.c.d";
long result = s.chars().filter(ch -> ch == '.').count();

короче пример

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

вот решение без цикла:

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

ну, есть цикл, но это невидимка : -)

-- Йонатан


мне не нравится идея выделения новой строки для этой цели. И поскольку строка уже имеет массив символов в задней части, где она хранит его значение, String.charAt () практически бесплатно.

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

делает трюк, без дополнительных распределений, которые нуждаются в сборе, в 1 строке или меньше, только с J2SE.


хорошо, вдохновленный решением Йонатана, вот один, который чисто рекурсивный-используются только методы библиотеки 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);
}

считается ли рекурсия циклической, зависит от того, какое точное определение вы используете, но это, вероятно, так близко, как вы получите.

Я не знаю, делает ли большинство JVMs хвостовую рекурсию в эти дни... если нет, вы получите одноименное переполнение стека для подходящих длинных строк, курс.


вдохновленный Джоном скитом, версией без цикла, которая не взорвет ваш стек. Также полезная отправная точка, если вы хотите использовать структуру 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;
}

Не уверен в эффективности этого, но это самый короткий код, который я мог бы написать без привлечения сторонних библиотек:

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

С java-8 вы также можете использовать потоки для достижения этой цели. Очевидно, что за кулисами есть итерация, но вам не нужно писать ее явно!

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

полный пример:

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

самый простой способ получить ответ следующим образом:

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

Если вы используете Spring framework, вы также можете использовать класс "StringUtils". Метод будет "countOccurrencesOf".


также можно использовать reduce в Java 8 для решения этой проблемы:

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

выход:

3

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

можно использовать split() функция только в одной строке кода

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

хотя методы могут скрыть его, нет способа подсчета без цикла (или рекурсии). Однако вы хотите использовать 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) не похоже на лучший способ пойти.


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

String[] parts = text.split(".");
int occurances = parts.length - 1;

" It's a great day at O.S.G. Dallas! "
     -- Famous Last Words

Ну, это случай знания вашей Java, особенно вашего базового понимания классов коллекции, уже доступных в Java. Если вы посмотрите на всю публикацию здесь, есть почти все, кроме объяснения Стивена Хокинга о происхождении Вселенной, книги Дарвина об эволюции и выбора Джина Родденберри "Звездный путь" о том, почему они пошли с Уильямом Шатнером, как это сделать быстро и легко...

... нужно ли еще что-то говорить?


где-то в коде, что-то петли. Единственный способ обойти это-полное развертывание цикла:

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

вот немного другое решение рекурсии стиля:

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

Почему бы просто не разделить символ, а затем получить длину результирующего массива. длина массива всегда будет числом экземпляров + 1. Правильно?


следующий исходный код даст вам нет.вхождений заданной строки в слово, введенное пользователем : -

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

    }
}

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

используя Коллекции Eclipse

int count = CharAdapter.adapt("a.b.c.d").count(c -> c == '.');

Если у вас есть более одного символа для подсчета, вы можете использовать CharBag следующим образом:

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

Примечание: я коммиттер для коллекций Eclipse.