從字符串中查找字符出現次數的方法和性能對比

今天在一場“特殊的討論”中引入了一個問題,如何在C#求出字符串中某字符的出現次數,比如求“ADSFGEHERGASDF”中“A”出現的次數。首先想到的方法當然是從頭遍歷字符串並統計:

c1 = 0;
for (int i = 0; i < str.Length; i++)
{
    if (str[i] == 'A')
    {
        c1++;
    }
}

第二種方法也很容易想到,將字符串中所有要查找的字符去除,然後比較去除前後的字符串長度即可。這種方法遭到了某人的鄙視,據說性能很差而且多佔空間。

c2 = str.Length - str.Replace("A", String.Empty).Length;

接下來某人又提出了第三種方法,是用要查找的字符爲分隔符,將原字符串分隔爲多個子串,然後求子串的數目即可。在C#中這是一個寫起來很短的方法:

c3 = str.Split(new char[] { 'A' }).Length - 1;

我們從原理可以推斷出三者性能的順序,但究竟差距是多少呢,還是要動手試驗一下。這是非常經典的測試代碼:

string str = "SADTHDGSAFSDGTGHRDGSADFADDRHDFSGASDAA";

Stopwatch sw = new Stopwatch();

long t;
int c = 0;
GC.Collect();
Application.DoEvents();

sw.Start();

for (int i = 0; i < 100000; i++)
{
    c = 三種算法
}

sw.Stop();

t = sw.ElapsedMilliseconds;

首先我們確保正確性,經測試三種方法都能正確處理多種情況,包括首尾、連續出現、不出現或串長度爲0等,我所取的字符串是一個很普通的串。編譯爲Release版,預運行10次後獲得以下結果:

遍歷統計:13毫秒
替換後比較長度:112毫秒
斷開字符串後計數:233毫秒

這裏已經體現出差異,遍歷統計比替換後比較要快10倍,斷開字符串又要慢一些。接下來我又做了如下兩個測試:

1、不改變字符串的長度,增加或減少要查找字符串的個數。
2、不改變要查找字符出現的頻率,但增長字符串的長度。

結果發現,三種方法都隨字符串長度增加線性變慢,而後兩種方法還隨要查找的字符增加而變慢。斷開字符串的方法還受要查找字符串分佈情況的影響。

研究Replace函數和Split函數的實現可以徹底解決這個問題。不過我沒有心情細細研究了,我還是決定選用第二種方法——替換後比較長度。雖然其速度比第一種方法慢,但易於改寫爲求長度不爲1的子串出現次數的方法。第一種方法若改爲求長度大於1的字串就要考慮很多因素(儘管不一定真的很麻煩),我懶得想了,呵呵。

打印 | posted on 2004年11月23日 18:08 | Filed Under [ 技術隨筆 ] | 收藏本頁 (百度搜藏)(QQ書籤)(Live收藏)(Google書籤)(Yahoo書籤)(新浪ViVi)(搜狐網摘)(365Key網摘)(天極網摘)(博採網摘)(和訊網摘)

href="http://blog.joycode.com/ninputer/Services/Pingback.aspx" rel="pingback" />

反饋

Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
如果用第一種方法,子串越長,效率越高。連測試代碼都寫了,也提了某人的名字n次,還在乎這點小事:
if (str[i] == 'A')
{
c1++;
}
比較改爲strncmp,然後i=i+子串長度即可,

:-)
2004/11/23 19:18 | bruise
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
難道不能用正則表達式?
2004/11/23 20:10 | bestcomy
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
其實方法多得要死,我只是閒暇無事測來看看。不要太當真薩。

很多方法要做字符串長度判斷或者邊界條件判斷,如此簡單又不是特別需要性能的地方就從簡考慮哈
2004/11/23 20:26 | Ninputer
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
我通常使用第二種。
畢竟效率不是唯一考慮的因素。
2004/11/24 8:40 | jiezhi
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
能否講解一下Stopwatch?
2004/11/24 9:54 | kkk
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
拜託,1,3不是我昨天寫的code?
不尊重我的知識產權,你更要請客了
2004/11/24 11:19 | 小峯
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
我就知道一堆MVP跑不出什麼別的題來……
2004/11/24 12:25 | jiangsheng
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
我怎麼覺得使用除過1之外的方法時,已經包含了1的方法;
這樣所有其它的方法必然比1的方法效率低.
應爲不論採用什麼方法都要識別出待查找的字符串,這樣已經可以計數了呀,爲什麼還要做別的事?
當然邊界條件是每個程序都應該考慮的.
2004/11/25 17:30 | iami
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
To bruise :
爲什麼我用以下代碼反而最費時,而且字符串越長,性能差異越明顯,遠不及Replace快,甚至比不上Spilit
int c = 0;
for (int i = 0; i < sourceString.Length-childString.Length+1; i++)
{
if (string.Compare(sourceString,i,childString,0,childString.Length) == 0)
{
c++;
i += childString.Length;
}
}
不過自己寫的
int c = 0;
for (int i = 0; i < sourceString.Length-childString.Length+1; i++)
{
int iMatch = 0;
for (int j = 0; j < childString.Length; j++)
{

if (sourceString[i + iMatch] != childString[j])
{
break;

}
else
{
iMatch++;
}
}
if (iMatch == childString.Length)
{
c++;
i += childString.Length;
}
}
倒是比string.Compare快,不過還是不及Replace
迷惑。。。。。。。
2004/11/28 22:01 | Flyfox
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
最快當然是直接對內存操作,通過對象來操作本來就慢了,對象的replace可能內部實現會比較優化,而自己寫算法,卻以對象爲操作單位,開銷會大也很正常……
2004/12/23 16:28 | superliufa
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
在循環中使用對象時,應該用固定的變量來代替對象,比如:
for (int i = 0; i < sourceString.Length-childString.Length+1; i++)
可以換成
int Slen = sourceString.Length;
int Clen = childString.Length;
for (int i = 0; i < Slen -Clen +1; i++)
{
}
2005/1/4 10:57 | CSQ_CPU
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
仁兄做的例程沒有代表性,因爲在源書符串查詢的子符正好長充爲1,如果長度大於1.那麼第二種方法能突出其優勢.
2005/6/8 11:10 | 老劉
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
能否給一個完整的程序
2005/9/10 9:33 | meilidexinshijie
Gravatar
# re: 從字符串中查找字符出現次數的方法和性能對比
都什麼年代了,用的又不是8086,快點實現最重要,要不然市場早就被他人佔了.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章