Wystarczy wskazać - doceniam znalezienie liniałów jednorzędowych, jest to zabawne i (jako prawdziwa zaleta) często łatwe do zapamiętania, ale chciałbym zauważyć, że osobna metoda i pętla są lepsze pod każdym względem - czytelność, a nawet wydajność. Większość opisanych poniżej „eleganckich” rozwiązań nie będzie działać bardzo dobrze, ponieważ wymagają one reformowania łańcuchów / kopiowania pamięci, podczas gdy pętla, która skanowała łańcuch i liczone wystąpienia, byłaby szybka i prosta. Nie to, że wydajność powinna być na ogół czynnikiem, ale nie patrz na jedną linię w pętli i nie zakładaj, że będzie działać lepiej.
Bill K
Odpowiedzi:
722
Mój „idiomatyczny jedno-liniowiec” do tego to:
int count =StringUtils.countMatches("a.b.c.d",".");
To, co było drogie, w każdej firmie, w której pracowałem, ma wiele źle napisanych i źle utrzymanych klas „* Utils”. Częścią twojego zadania jest wiedzieć, co jest dostępne w Apache Commons.
AbuNassar,
1016
Co powiesz na to. Nie używa wyrażeń regularnych pod spodem, więc powinno być szybsze niż niektóre inne rozwiązania i nie będzie używać pętli.
int count = line.length()- line.replace(".","").length();
Najprostszy sposób. Sprytny. I działa na Androidzie, gdzie nie ma klasy
StringUtils
43
To najlepsza odpowiedź. Najlepszym powodem jest to, że nie musisz importować innej biblioteki.
Alex Spencer,
27
Bardzo praktyczne, ale brzydkie jak diabli. Nie polecam tego, ponieważ prowadzi to do mylącego kodu.
Daniel San
32
Brzydki kod można zminimalizować, czyniąc go metodą we własnej klasie „StringUtils”. Wtedy brzydki kod jest dokładnie w jednym miejscu i wszędzie indziej jest dobrze czytelny.
RonR
30
Metoda pętli jest znacznie szybsza. Zwłaszcza gdy chcesz policzyć znak zamiast ciągu znaków (ponieważ nie ma metody String.replace (char, char)). W ciągu 15 znaków otrzymuję różnicę 6049 ns vs 26.739 ns (uśrednione po 100 biegach). Surowe liczby są ogromną różnicą, ale pod względem procentowym ... sumuje się. Unikaj przydziału pamięci - użyj pętli!
Ben
282
Podsumuj inną odpowiedź i to, co wiem na wszystkie sposoby, aby to zrobić za pomocą jednej linijki:
String testString ="a.b.c.d";
1) Korzystanie z Apache Commons
int apache =StringUtils.countMatches(testString,".");System.out.println("apache = "+ apache);
2) Korzystanie z Spring Framework
int spring = org.springframework.util.StringUtils.countOccurrencesOf(testString,".");System.out.println("spring = "+ spring);
3) Za pomocą zamiany
int replace = testString.length()- testString.replace(".","").length();System.out.println("replace = "+ replace);
4) Korzystanie z replaceAll (przypadek 1)
int replaceAll = testString.replaceAll("[^.]","").length();System.out.println("replaceAll = "+ replaceAll);
5) Korzystanie z replaceAll (przypadek 2)
int replaceAllCase2 = testString.length()- testString.replaceAll("\\.","").length();System.out.println("replaceAll (second case) = "+ replaceAllCase2);
6) Korzystanie z podziału
int split = testString.split("\\.",-1).length-1;System.out.println("split = "+ split);
7) Korzystanie z Java8 (przypadek 1)
long java8 = testString.chars().filter(ch -> ch =='.').count();System.out.println("java8 = "+ java8);
8) Korzystanie z Java8 (przypadek 2) może być lepsze dla Unicode niż przypadek 1
int stringTokenizer =newStringTokenizer(" "+testString +" ",".").countTokens()-1;System.out.println("stringTokenizer = "+ stringTokenizer);
Z komentarza : Uważaj na StringTokenizer, dla abcd zadziała, ale dla ... bc ... d lub ... abcd lub a ... b ...... c ..... d ... lub itp. to nie zadziała. To się po prostu liczy. między postaciami tylko raz
Wydrukowane ciągi nie pasują do powyższych, a kolejność jest najpierw najszybsza, co sprawia, że wyszukiwanie jest co najmniej trudne. Dobra odpowiedź poza tym!
Maarten Bodewes
przypadek 2, uogólniony dla "1🚲2🚲3 has 2".codePoints().filter((c) -> c == "🚲".codePointAt(0)).count()
punktów kodowych
174
Wcześniej czy później coś musi się zapętlić. O wiele łatwiej jest napisać (bardzo prostą) pętlę niż użyć czegoś, splitco jest o wiele potężniejsze niż potrzebujesz.
for (int i = 0, l = haystack.length (); i <l; i ++) bądź miły dla swojego stosu
Chris,
12
(Nie jestem nawet pewien, skąd pochodzi fragment komentarza „stos”. To nie jest tak, że moja odpowiedź jest rekurencyjna, co jest naprawdę nieprzyjemne dla stosu.)
Jon Skeet
2
nie tylko to, ale prawdopodobnie jest to anty optymalizacja bez spojrzenia na to, co robi jit. Jeśli zrobiłeś powyższe na przykład dla tablicy for loop, możesz pogorszyć sytuację.
ShuggyCoUk
4
@sulai: Troska Chrisa jest bezpodstawna, IMO, w obliczu trywialnej optymalizacji JIT. Czy jest jakiś powód, dla którego ten komentarz zwrócił Twoją uwagę ponad trzy lata później? Po prostu zainteresowany.
Jon Skeet
1
Prawdopodobnie @sulai po prostu natknęłam się na to pytanie, tak jak ja (zastanawiając się, czy Java ma wbudowaną metodę tego) i nie zauważyła dat. Jestem jednak ciekawy, jak przeniesienie length()wywołania poza pętlę może pogorszyć wydajność , o czym wspomniał @ShuggyCoUk w kilku komentarzach.
JKillian
63
Miałem pomysł podobny do Mladena, ale odwrotnie ...
String s ="a.b.c.d";int charCount = s.replaceAll("[^.]","").length();
println(charCount);
Poprawny. ReplaceAll („.”) Zastąpi dowolny znak, nie tylko kropkę. Zastąpiłoby wszystko („\\.”) Działałoby. Twoje rozwiązanie jest prostsze.
VonC,
jjnguy faktycznie zasugerował najpierw replaceAll („[^.]”), po zobaczeniu mojego rozwiązania „abcd” .split („\\.”). length-1. Ale po pięciokrotnym trafieniu usunąłem swoją odpowiedź (i jego komentarz).
VonC
„... teraz masz dwa problemy” (oblig.) W każdym razie, założę się, że istnieją dziesiątki pętli wykonywany w replaceAll()i length(). Cóż, jeśli nie jest widoczny, to nie istnieje; o)
Piskvor opuścił budynek
2
nie sądzę, że dobrym pomysłem jest użycie wyrażenia regularnego i utworzenie nowego ciągu do liczenia. po prostu stworzę metodę statyczną, która zapętli każdy znak w ciągu, aby policzyć liczbę.
mingfai,
1
@mingfai: wprawdzie, ale pierwotne pytanie dotyczy tworzenia jednowierszowej, a nawet bez pętli (możesz zrobić pętlę w jednej linii, ale będzie brzydka!). Pytanie pytanie, a nie odpowiedź ... :-)
PhiLho
37
String s ="a.b.c.d";int charCount = s.length()- s.replaceAll("\\.","").length();
ReplaceAll („.”) Zastąpi wszystkie znaki.
Rozwiązanie PhiLho wykorzystuje ReplaceAll („[^.]”, „”), Którego nie trzeba usuwać, ponieważ [.] Reprezentuje znak „kropka”, a nie „dowolny znak”.
W tym poście jest starsze rozwiązanie podobne do tego.
JCalcines
7
Ponieważ to rozwiązanie jest naprawdę nieefektywne
András
To tworzy dodatkowy ciąg znaków, aby wygenerować liczbę. Nie mam pojęcia, dlaczego ktokolwiek wolałby to od StringUtils, jeśli StringUtils jest opcją. Jeśli nie jest to opcja, powinni po prostu stworzyć prostą pętlę for w klasie narzędzi.
zmiażdżyć
28
String s ="a.b.c.d";long result = s.chars().filter(ch -> ch =='.').count();
Ten wydaje się mieć stosunkowo duży narzut, ostrzegam, że może tworzyć wiele małych łańcuchów. Zwykle nie ma to większego znaczenia, ale należy zachować ostrożność.
Maarten Bodewes
19
oto rozwiązanie bez pętli:
publicstaticint 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));
O ile łańcuch nie jest tak długi, otrzymasz OutOfMemoryError.
Spencer Kormos
Wydaje się, że problem jest na tyle zmyślny, że może być pracą domową, a jeśli tak, to prawdopodobnie rekursja jest odpowiedzią, którą należy znaleźć.
erickson,
Używa indexOf, który zapętli ... ale fajny pomysł. Publikowanie naprawdę „tylko rekurencyjnego” rozwiązania za minutę ...
Jon Skeet,
Jeśli ma więcej wystąpień niż twoje dostępne miejsca na stosie, będziesz miał wyjątek przepełnienia stosu;)
Luca C.
15
Nie podoba mi się pomysł przydzielenia nowego ciągu do tego celu. A ponieważ ciąg znaków ma już tablicę char z tyłu, w której przechowuje swoją wartość, String.charAt () jest praktycznie wolny.
Uwielbiam ten, ponieważ jest on jedyną osobą wykonującą jedno przejście po sznurku. Zależy mi na wydajności.
javadba
1
charAtiteruje 16-bitowe punkty kodu, a nie znaki! A charw Javie nie jest znakiem. Tak więc ta odpowiedź sugeruje, że nie może być żadnego symbolu Unicode z wysoką wartością zastępczą równą punktowi kodowemu delim. Nie jestem pewien, czy jest poprawny dla kropki, ale ogólnie może być nieprawidłowy.
ceving
14
Ok, zainspirowany rozwiązaniem Yonatan, oto metoda czysto rekurencyjna - jedynymi używanymi metodami bibliotecznymi są length()i charAt()żadna z nich nie zapętla:
To, czy rekurencja liczy się jako zapętlenie, zależy od dokładnej definicji, której używasz, ale prawdopodobnie jest ona tak bliska, jak to tylko możliwe.
Nie wiem, czy obecnie większość maszyn JVM wykonuje rekursję ogona ... jeśli nie, oczywiście otrzymasz przepełnienie stosu dla odpowiednio długich łańcuchów.
Nie, rekurencja ogona prawdopodobnie będzie w Javie 7, ale nie jest jeszcze powszechna. Ta prosta, bezpośrednia rekurencja może być przetłumaczona na pętlę w czasie kompilacji, ale Java 7 jest tak naprawdę wbudowana w JVM, aby obsługiwać łańcuch przy użyciu różnych metod.
erickson,
3
Bardziej prawdopodobne byłoby uzyskanie rekurencji ogona, gdyby metoda zwróciła sobie wywołanie (w tym działający parametr sumaryczny), zamiast zwracać wynik dodawania.
Stephen Denne
12
Zainspirowany przez Jona Skeeta, wersję bez pętli, która nie wysadzi twojego stacka. Przydatny również punkt początkowy, jeśli chcesz korzystać ze szkieletu łączenia widelca.
publicstaticint 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].privatestaticint countOccurrences(CharSequence haystack,char needle,int start,int end
){if(start == end){return0;}elseif(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);}}
(Zastrzeżenie: Nie przetestowano, nie skompilowano, nie ma sensu.)
Być może najlepszy sposób na napisanie tego (jednowątkowy, bez pary zastępczej):
Aby liczyć wystąpień na końcu łańcucha trzeba będzie wywołać rozłam z limitem ujemne argumentu takiego: return (content.split(target, -1).length - 1);. Domyślnie wystąpienia na końcu łańcucha są pomijane w tablicy wynikającej z split (). Zobacz Doku
vlz
10
Z java-8możesz również użyć strumieni, aby to osiągnąć. Oczywiście za kulisami jest iteracja, ale nie musisz pisać tego wprost!
Split naprawdę tworzy tablicę ciągów, co zajmuje dużo czasu.
Palec
Masz rację, to prawdziwa troska. W inny sposób unika wprowadzania biblioteki osób trzecich do twojego projektu (jeśli jeszcze tego nie zrobiono). To zależy od tego, co chcesz zrobić i jakie są oczekiwania dotyczące wydajności.
Benj
3
To rozwiązanie NIE będzie zawierać końcowych pustych trafień, ponieważ argument limitjest ustawiony na zero w tym przeciążonym wywołaniu metody split. Przykład: "1##2#3#####".split("#")zwróci tylko tablicę o rozmiarze 4 ( [0:"1";1:""; 2:"2"; 3:"3"]) zamiast wielkości 9 ( [0:"1"; 1:""; 2:"2"; 3:"3"; 4:""; 5:""; 6:""; 7:""; 8:""]).
import java.util.Scanner;class apples {publicstaticvoid main(String args[]){Scanner bucky =newScanner(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
Myślę, że to najbardziej eleganckie rozwiązanie. Dlaczego użyłeś toCharArray, a nie charAt bezpośrednio?
Panayotis
Pętla z charAt przynajmniej była wolniejsza. Może zależeć również od platformy. Jedynym sposobem, aby naprawdę się dowiedzieć, jest zmierzenie różnicy.
tcurdt
3
Z całkiem podobnym zadaniem natknąłem się na ten wątek. Nie widziałem żadnych ograniczeń języka programowania, a ponieważ groovy działa na Javie vm: Oto, jak mogłem rozwiązać mój problem za pomocą Groovy.
Problem polega na tym, że tablica musi być przydzielona, co jest strasznie wolne.
Palec
2
Gdzieś w kodzie coś musi zapętlić. Jedynym sposobem na obejście tego jest całkowite rozwinięcie pętli:
int numDots =0;if(s.charAt(0)=='.'){
numDots++;}if(s.charAt(1)=='.'){
numDots++;}if(s.charAt(2)=='.'){
numDots++;}
... itd., ale to Ty robisz pętlę ręcznie w edytorze źródłowym - zamiast komputera, który go uruchomi. Zobacz pseudokod:
create a project
position =0while(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
Odpowiedzi:
Mój „idiomatyczny jedno-liniowiec” do tego to:
Po co pisać, gdy jest już w języku wspólnym ?
Oneliner firmy Spring Framework to:
źródło
int count = CharMatcher.is('.').countIn("a.b.c.d");
... Jak odpowiedział dogbane w duplikacie pytania.Co powiesz na to. Nie używa wyrażeń regularnych pod spodem, więc powinno być szybsze niż niektóre inne rozwiązania i nie będzie używać pętli.
źródło
Podsumuj inną odpowiedź i to, co wiem na wszystkie sposoby, aby to zrobić za pomocą jednej linijki:
1) Korzystanie z Apache Commons
2) Korzystanie z Spring Framework
3) Za pomocą zamiany
4) Korzystanie z replaceAll (przypadek 1)
5) Korzystanie z replaceAll (przypadek 2)
6) Korzystanie z podziału
7) Korzystanie z Java8 (przypadek 1)
8) Korzystanie z Java8 (przypadek 2) może być lepsze dla Unicode niż przypadek 1
9) Korzystanie z StringTokenizer
Z komentarza : Uważaj na StringTokenizer, dla abcd zadziała, ale dla ... bc ... d lub ... abcd lub a ... b ...... c ..... d ... lub itp. to nie zadziała. To się po prostu liczy. między postaciami tylko raz
Więcej informacji w github
Test wydajności (przy użyciu JMH , mode = AverageTime,
0.010
lepiej niż wynik0.351
):źródło
"1🚲2🚲3 has 2".codePoints().filter((c) -> c == "🚲".codePointAt(0)).count()
Wcześniej czy później coś musi się zapętlić. O wiele łatwiej jest napisać (bardzo prostą) pętlę niż użyć czegoś,
split
co jest o wiele potężniejsze niż potrzebujesz.Za wszelką cenę obuduj pętlę osobną metodą, np
Wtedy nie potrzebujesz pętli w głównym kodzie - ale pętla musi gdzieś tam być.
źródło
length()
wywołania poza pętlę może pogorszyć wydajność , o czym wspomniał @ShuggyCoUk w kilku komentarzach.Miałem pomysł podobny do Mladena, ale odwrotnie ...
źródło
replaceAll()
ilength()
. Cóż, jeśli nie jest widoczny, to nie istnieje; o)ReplaceAll („.”) Zastąpi wszystkie znaki.
Rozwiązanie PhiLho wykorzystuje ReplaceAll („[^.]”, „”), Którego nie trzeba usuwać, ponieważ [.] Reprezentuje znak „kropka”, a nie „dowolny znak”.
źródło
Moje rozwiązanie „idiomatic one-liner”:
Nie mam pojęcia, dlaczego akceptowane jest rozwiązanie korzystające z StringUtils.
źródło
źródło
Krótszy przykład to
źródło
oto rozwiązanie bez pętli:
Cóż, jest pętla, ale jest niewidoczna :-)
- Yonatan
źródło
Nie podoba mi się pomysł przydzielenia nowego ciągu do tego celu. A ponieważ ciąg znaków ma już tablicę char z tyłu, w której przechowuje swoją wartość, String.charAt () jest praktycznie wolny.
robi lewę, bez dodatkowych przydziałów, które wymagają zbierania, w 1 linii lub mniej, tylko z J2SE.
źródło
charAt
iteruje 16-bitowe punkty kodu, a nie znaki! Achar
w Javie nie jest znakiem. Tak więc ta odpowiedź sugeruje, że nie może być żadnego symbolu Unicode z wysoką wartością zastępczą równą punktowi kodowemudelim
. Nie jestem pewien, czy jest poprawny dla kropki, ale ogólnie może być nieprawidłowy.Ok, zainspirowany rozwiązaniem Yonatan, oto metoda czysto rekurencyjna - jedynymi używanymi metodami bibliotecznymi są
length()
icharAt()
żadna z nich nie zapętla:To, czy rekurencja liczy się jako zapętlenie, zależy od dokładnej definicji, której używasz, ale prawdopodobnie jest ona tak bliska, jak to tylko możliwe.
Nie wiem, czy obecnie większość maszyn JVM wykonuje rekursję ogona ... jeśli nie, oczywiście otrzymasz przepełnienie stosu dla odpowiednio długich łańcuchów.
źródło
Zainspirowany przez Jona Skeeta, wersję bez pętli, która nie wysadzi twojego stacka. Przydatny również punkt początkowy, jeśli chcesz korzystać ze szkieletu łączenia widelca.
(Zastrzeżenie: Nie przetestowano, nie skompilowano, nie ma sensu.)
Być może najlepszy sposób na napisanie tego (jednowątkowy, bez pary zastępczej):
źródło
Nie jestem pewien co do wydajności tego, ale jest to najkrótszy kod, jaki mógłbym napisać bez korzystania z bibliotek stron trzecich:
źródło
return (content.split(target, -1).length - 1);
. Domyślnie wystąpienia na końcu łańcucha są pomijane w tablicy wynikającej z split (). Zobacz DokuZ java-8możesz również użyć strumieni, aby to osiągnąć. Oczywiście za kulisami jest iteracja, ale nie musisz pisać tego wprost!
źródło
.codePoints()
zamiast.chars()
tego obsługiwałoby wtedy dowolną wartość Unicode (włączając te wymagające par zastępczych)Możliwe jest również użycie funkcji zmniejszania w Javie 8, aby rozwiązać ten problem:
Wynik:
źródło
Pełna próbka:
Połączenie:
źródło
Najprostszym sposobem na uzyskanie odpowiedzi jest:
źródło
Jeśli używasz frameworka Spring, możesz również użyć klasy „StringUtils”. Metodą będzie „countOccurrencesOf”.
źródło
Możesz użyć tej
split()
funkcji w jednym kodzie liniiźródło
limit
jest ustawiony na zero w tym przeciążonym wywołaniu metody split. Przykład:"1##2#3#####".split("#")
zwróci tylko tablicę o rozmiarze 4 ([0:"1";1:""; 2:"2"; 3:"3"]
) zamiast wielkości 9 ([0:"1"; 1:""; 2:"2"; 3:"3"; 4:""; 5:""; 6:""; 7:""; 8:""]
).źródło
źródło
Chociaż metody mogą to ukryć, nie ma możliwości liczenia bez pętli (lub rekurencji). Chcesz jednak użyć char [] ze względu na wydajność.
Korzystanie z replaceAll (czyli RE) nie brzmi jak najlepsza droga.
źródło
Z całkiem podobnym zadaniem natknąłem się na ten wątek. Nie widziałem żadnych ograniczeń języka programowania, a ponieważ groovy działa na Javie vm: Oto, jak mogłem rozwiązać mój problem za pomocą Groovy.
gotowy.
źródło
Znacznie łatwiejszym rozwiązaniem byłoby podzielenie łańcucha na podstawie znaku, z którym go dopasujesz.
Na przykład,
int getOccurences(String characters, String string) { String[] words = string.split(characters); return words.length - 1; }
Zwróci 4 w przypadku:
getOccurences("o", "something about a quick brown fox");
źródło
Gdzieś w kodzie coś musi zapętlić. Jedynym sposobem na obejście tego jest całkowite rozwinięcie pętli:
... itd., ale to Ty robisz pętlę ręcznie w edytorze źródłowym - zamiast komputera, który go uruchomi. Zobacz pseudokod:
źródło
Oto nieco inne rozwiązanie rekurencyjne w stylu:
źródło
Dlaczego nie po prostu podzielić na postać, a następnie uzyskać długość wynikowej tablicy. długość tablicy zawsze będzie liczbą wystąpień + 1. Prawda?
źródło
Poniższy kod źródłowy poda liczbę wystąpień danego ciągu w słowie wprowadzonym przez użytkownika: -
źródło
źródło