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

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

a.b.c.d

Я хочу посчитать случаи "." идиоматическим образом, предпочтительно однострочник.

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

48 ответов

Решение

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

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

Зачем писать самому, когда он уже в общем языке?

Единственный подход Spring Framework для этого:

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) Использование Spring Framework's

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), может быть лучше для юникода, чем случай 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

Тест производительности (с использованием JMH, режим = Среднее время, оценка 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 использует ReplaceAll("[^.]",""), Который не нужно экранировать, поскольку [.] Представляет символ "точка", а не "любой символ".

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

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

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

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

Более короткий пример

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

ну, есть петля, но она невидима:-)

- Йонатан

Мне не нравится идея выделения новой строки для этой цели. И так как строка уже имеет массив char сзади, где она хранит свое значение, 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);
}

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

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

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

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

Вдохновленный 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;
}

С 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

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

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

Выход:

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

Полный образец:

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

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

Гораздо более простым решением было бы просто разделить строку на основе символа, с которым вы ее сопоставляете.

Например,

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

Это вернет 4 в случае:getOccurences("o", "something about a quick brown fox");

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

int noOccurence=string.split("#").length-1;
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
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;
}

Это то, что я использую для подсчета вхождений строки.

Надеюсь, кто-то сочтет это полезным.

    private long countOccurrences(String occurrences, char findChar){
        return  occurrences.chars().filter( x -> {
            return x == findChar;
        }).count();
    }

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

Ну, с довольно похожей задачей я наткнулся на эту тему. Я не видел никаких ограничений в языке программирования, и поскольку groovy работает на Java vm: Вот как я смог решить свою проблему с помощью Groovy.

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

сделанный.

Использование коллекций 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 Collections.

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

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

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

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

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

    }
}
Другие вопросы по тегам