2012-07-24 61 views
2

我正在編寫一個驗證一些城市的應用程序。驗證的一部分是通過匹配國家代碼和城市名稱(或alt cityname)來檢查城市是否已經在列表中。最快的方法來比較c中的對象#

我儲存我現有的城市名單爲:

public struct City 
{ 
    public int id; 
    public string countrycode; 
    public string name; 
    public string altName; 
    public int timezoneId; 
} 

List<City> cityCache = new List<City>(); 

我那麼有包含國家代碼和城市名稱等我拆分此字符串,然後檢查如果城市已經存在位置的字符串列表。

string cityString = GetCity(); //get the city string 
string countryCode = GetCountry(); //get the country string 
city = new City();    //create a new city object 
if (!string.IsNullOrEmpty(cityString)) //don't bother checking if no city was specified 
{ 
    //check if city exists in the list in the same country 
    city = cityCache.FirstOrDefault(x => countryCode == x.countrycode && (Like(x.name, cityString) || Like(x.altName, cityString))); 
    //if no city if found, search for a single match accross any country 
    if (city.id == default(int) && cityCache.Count(x => Like(x.name, cityString) || Like(x.altName, cityString)) == 1) 
     city = cityCache.FirstOrDefault(x => Like(x.name, cityString) || Like(x.altName, cityString)); 
} 

if (city.id == default(int)) 
{ 
    //city not matched 
} 

這對於很多記錄來說非常慢,因爲我也以同樣的方式檢查機場和國家等其他對象。有什麼辦法可以加快速度嗎?這種比較比List <>有更快的收集,並且有更快的比較函數FirsOrDefault()嗎?

編輯

我忘了我的後贊()函數:

bool Like(string s1, string s2) 
    { 
     if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2)) 
      return s1 == s2; 
     if (s1.ToLower().Trim() == s2.ToLower().Trim()) 
      return true; 

     return Regex.IsMatch(Regex.Escape(s1.ToLower().Trim()), Regex.Escape(s2.ToLower().Trim()) + "."); 
    } 
+0

我相信你最大的性能問題是'Like'運營商,這是昂貴的。你不能簡單地使用一個相等比較器嗎? – 2012-07-24 13:06:06

+0

你能告訴我們你是怎麼稱呼這個比較方法 – 2012-07-24 13:09:36

+0

我建議不要在內存中這樣做,原因有兩個。首先是因爲你已經看到這個機制存在明顯的性能問題,其次是因爲你在內存中保存了大量的信息,嚴格來說是爲了搜索它。這適用於數據庫服務器,並且往返開銷非常微不足道。 – 2012-07-24 13:15:26

回答

1

我會用一個HashSet的CityString和COUNTRYCODE。 喜歡的東西

var validCountryCode = new HashSet<string>(StringComparison.OrdinalIgnoreCase); 
if (validCountryCode.Contains(city.CountryCode)) 
{ 
} 

等等

個人而言,我會做所有的驗證在構造函數中,以確保只有合法市物體存在。

其他的事情需要提防的性能

  1. 使用HashSet的,如果你在一個有效的清單中找到它。
  2. 在適當情況下使用IEqualityComparer,重用該對象以避免構建/ GC成本。
  3. 使用字典任何你需要查找(如timeZoneId)

編輯1

你cityCache就應該像這樣,

var cityCache = new Dictionary<string, Dictionary<string, int>>(); 
var countryCode = ""; 
var cityCode = ""; 
var id = x; 

public static IsCityValid(City c) 
{ 
    return 
     cityCache.ContainsKey(c.CountryCode) && 
     cityCache[c.CountryCode].ContainsKey(c.CityCode) && 
     cityCache[c.CountryCode][c.CityCode] == c.Id; 
} 

編輯2

沒想到我必須解釋這一點,但基於評論,也許。

FirstOrDefault()是O(n)操作。基本上,每當你試圖在列表中找到某物時,你可以是幸運的,它是列表中的第一個,或者是不幸的,它是list.Count/2的最後一個平均值。另一方面,字典將是O(1)查找。使用IEqualtiyComparer它將生成一個HashCode()並查找它所在的桶。如果只有大量的衝突,那麼它將使用Equals來查找同一個桶中事物列表中的內容。即使是質量差的HashCode()(因爲總是返回相同的HashCode),因爲Dictionary/HashSet使用素數桶,您將分割列表,減少您需要完成的Equalities數量。

因此,10個對象的列表意味着你平均運行LIKE 5次。 與以下相同的10個對象(取決於HashCode的質量)的字典可能少至一個HashCode()調用,然後調用一個Equals()調用。

+0

請注意,代碼使用Like運算符 - 此代碼將不起作用,除非發生更改。 – 2012-07-24 13:25:19

+0

通過將正確的IEqualityComparer傳遞到Dictionary中,可以很容易地進行修復。然後,ContainsKey會匹配您正在執行的操作。 – 2012-07-24 13:27:33

+0

因此,如果您重新實施Like操作,那麼您並未解決性能問題 - 是的?這顯然是一個非常大的數據集,所以做一個Like需要掃描,因爲如果它是直接相等的話,還有其他選項 - 比如二分查找等等。如果Like是必需的 - 讓數據庫引擎執行它,因爲它適合它。 – 2012-07-24 13:32:20

0

這聽起來像是一個二叉樹的好候選者。

對於.NET二叉樹的實現,請參閱:Objects that represent trees

編輯:
如果你想快速搜索一個集合,該集合是特別大,那麼你最好的選擇是排序並基於該排序實現搜索算法。

當您想快速搜索並插入相對較少的項目時,二叉樹是一個不錯的選擇。然而,爲了保持你的搜索速度,你需要使用一個平衡二叉樹。

爲了能夠正常工作,您還需要一個用於城市的標準密鑰。數字鍵最好,但字符串也可以正常工作。如果您將城市與其他信息(如州和國家)連接起來,您將獲得一個不錯的唯一密鑰。您還可以將案例更改爲全部大寫或小寫以獲取不區分大小寫的密鑰。

如果您沒有密鑰,則無法對數據進行排序。如果你無法對數據進行排序,那麼就不會有很多「快速」選項。

編輯2:
我注意到你的函數編輯你的字符串很多。編輯字符串是一項非常昂貴的操作。您最好在執行ToLower()Trim()函數一次時,最好是在您首次加載數據時。這可能會大大加快你的功能。

+0

爲什麼你會使用二叉樹來搜索當前的無序列表? – 2012-07-24 13:40:24

+0

@DanPuzey - 海報說他正在建立自己的名單。如果他使用二叉樹,它不會無序。 (或者,用戶可以輕鬆地從無序列表中構建二叉樹。) – JDB 2012-07-24 13:41:39

+0

@DanPuzey - 還想補充一點,您不能用二叉樹「搜索」。你的問題沒有意義。 (您可以搜索二叉樹,但這意味着您需要先構建樹。) – JDB 2012-07-24 14:09:47