2011-11-16 25 views
13

Tôi có danh sách khoảng 10.000 nhân viên trong một List<T> và tôi có một số ListBox chứa một tập con của những nhân viên đó, tùy thuộc vào cụm từ tìm kiếm trong hộp văn bản.Kết hợp chuỗi tốc độ cao trong C#

Nói một đối tượng Staff có các tính chất tiếp xúc công khai sau đây:

string FirstName 
string LastName 
string MiddleName 
    int StaffID 
    int CostCentre 

tôi có thể viết một hàm như thế này:

bool staffMatchesSearch(Staff stf) 
{ 
    if (tbSrch.Text.Trim() == string.Empty) 
    return true; // No search = match always. 

    string s = tbSrch.Text.Trim().ToLower(); 

    // Do the checks in the order most likely to return soonest: 
    if (stf.LastName.ToLower().Contains(s)) 
    return true; 
    if (stf.FirstName.ToLower().Contains(s)) 
    return true; 
    if (stf.MiddleName.ToLower().Contains(s)) 
    return true; 

    if (stf.CostCentre.ToString().Contains(s)) 
    return true; // Yes, we want partial matches on CostCentre 
    if (stf.StaffID.ToString().Contains(s)) 
    return true; // And also on StaffID 

    return false; 
} 

và sau đó làm điều gì đó như:

tbSrch_TextChanged(object sender, EventArgs e) 
{ 
    lbStaff.BeginUpdate(); 
    lbStaff.Items.Clear(); 

    foreach (Staff stf in staff) 
    if (staffMatchesSearch(stf)) 
     lbStaff.Items.Add(stf); 

    lbStaff.EndUpdate(); 
} 

Bộ lọc được đánh giá lại mỗi khi người dùng thay đổi nội dung của ô tbSrch.

Làm việc này và không phải là awfully chậm, nhưng tôi đã tự hỏi liệu mình có thể làm nhanh hơn không?

Tôi đã cố gắng viết lại toàn bộ nội dung là đa luồng, tuy nhiên chỉ có 10.000 nhân viên ở trên cao dường như lấy đi phần lớn lợi ích. Ngoài ra, có một loạt các lỗi khác như tìm kiếm "John", trước tiên người dùng nhấn "J" để cuộn lên các chuỗi, nhưng khi người dùng nhấn "o", một bộ khác sẽ được đặt lên trước khi lô đầu tiên có một cơ hội để trả lại kết quả của họ. Rất nhiều thời gian, kết quả được trả lại trong một trật tự lộn xộn và tất cả những điều khó chịu xảy ra.

Tôi có thể nghĩ đến một vài chỉnh sửa sẽ làm cho kịch bản tốt nhất tốt hơn đáng kể, nhưng chúng cũng sẽ khiến kịch bản xấu nhất tồi tệ hơn nhiều.

Bạn có ý tưởng nào về cách cải thiện điều này không?


gợi ý tuyệt vời tôi đã thực hiện đến nay, và kết quả của họ:

  • Thêm một sự chậm trễ trên các sự kiện ValueChanged do đó nếu người dùng gõ một tên 5 ký tự một cách nhanh chóng trên bàn phím, nó chỉ thực hiện 1 tìm kiếm ở cuối thay vì 5 trong chuỗi.
  • Đánh giá trước ToLower() và lưu trữ trong lớp Staff (dưới dạng thuộc tính [NonSerialized] để nó không chiếm nhiều không gian hơn trong tệp lưu).
  • Thêm một tài sản get trong Staff trả về tất cả các tiêu chí tìm kiếm dưới dạng chuỗi đơn, dài, thấp hơn, được nối. Sau đó chạy một đơn Contains() trên đó. (String này được lưu trữ trong đối tượng Staff vì vậy nó chỉ được xây dựng một lần.)

Cho đến nay, những điều này đã làm giảm thời gian tìm kiếm từ khắp nơi trên 140ms xuống còn khoảng 60ms (mặc dù những con số này rất chủ quan tùy thuộc vào việc tìm kiếm thực tế thực hiện và số lượng kết quả được trả lại).

+0

bạn có thực sự muốn cho 'toString' những' int's? Có vẻ như bạn muốn có một phương thức chuỗi phù hợp và phù hợp với phương pháp int ... Ý tôi là, nếu tôi ở trung tâm 13, tôi không nên xuất hiện vì ai đó tìm kiếm trung tâm 1 hoặc trung tâm 3 ... – corsiKa

+0

Hãy thử triển khai một phương pháp trong chuỗi các thuật toán tìm kiếm chuỗi Boyer-Moore? Tiền xử lý hoặc thuật ngữ tìm kiếm, hoặc các đối tượng Staff và tái sử dụng kết quả có thể tiết kiệm rất nhiều thời gian. – millimoose

+0

Bạn có thực sự muốn tìm kiếm tất cả các kết quả phù hợp cho từng thuộc tính của nhân viên mỗi lần không? Là người dùng, tôi chỉ muốn tìm kiếm trên một hoặc hai trường đã biết tại một thời điểm. – ChandlerPelhams

Trả lời

7

1) như chỉ ra trong các ý kiến, có lẽ bạn không nên ToString các trường số - chỉ phù hợp với những con số

2) các cuộc gọi ToLower là một cống về hiệu suất.Thêm phiên bản thấp hơn của các thuộc tính này vào lớp Staff để ToLower chỉ phải được thực hiện sau khi

3) khi người dùng nhập một ký tự khác, bạn không cần đánh giá lại tất cả mục. việc nhập một ký tự sẽ chỉ giảm số lượng các kết quả phù hợp để chỉ đánh giá lại các trận đấu trước đó.

4) sử dụng bộ hẹn giờ để giới thiệu độ trễ giữa thời điểm người dùng nhập và khi bạn bắt đầu tìm kiếm. nếu họ đang nhập nhiều ký tự một cách nhanh chóng, bạn cũng có thể đợi cho đến khi chúng bị tạm dừng trong nửa giây

5) Kiểm tra phím đã được nhấn. Nếu NaN sau đó không kiểm tra các thuộc tính int.

+2

"khi người dùng nhập một nhân vật khác, bạn không cần phải đánh giá lại tất cả các mục. Nhập một ký tự sẽ chỉ giảm số lượng kết quả phù hợp để chỉ đánh giá lại các kết quả phù hợp trước đó "Điều này sẽ chỉ hoạt động nếu chúng nhập ký tự bổ sung ở cuối. Đó là một trường hợp tối ưu hóa đặc biệt. Có thể đáng làm, nhưng nó sẽ không tăng tốc tất cả các hoạt động, và có thể làm cho logic phức tạp hơn cho các hoạt động khác. –

+1

@ MerlynMorgan-Graham điểm công bằng nhưng trường hợp đặc biệt này có lẽ là trường hợp phổ biến nhất vì vậy có thể đáng giá mã phức tạp –

+1

Có lẽ: Kiểm tra phím được nhấn - nếu NaN của nó thì không kiểm tra thuộc tính int. – BlueChippy

2

Bạn có thể thêm thuộc tính riêng tư 'SearchTerm' vào đối tượng Staff là (FirstName + LastName + MiddleName + StaffID + CostCentre).ToLower() và thay vào đó hãy thực hiện kiểm tra Contains(). Điều này sẽ ngăn chặn bạn phải làm một ToLower() trên mỗi chuỗi mỗi lần và giảm số lượng các Contains() kiểm tra từ 5 đến 1.

+0

Sự giảm giá này chứa các cuộc gọi sẽ có những lợi ích không đáng kể (một cuộc gọi lớn đang làm nhiều công việc như những người nhỏ hơn) –

+0

Có những thay đổi đối với Contains() sẽ không phải là một sự khác biệt lớn, tôi nghĩ rằng lợi ích chính của phương pháp này sẽ loại bỏ sự cần thiết phải tiếp tục gọi ToLower() mỗi lần. – mikel

+0

Vấn đề là, tôi đã cố tình chia nó thành nhiều 'if' để trong trường hợp tốt nhất, nó sẽ giải cứu với một 'return true' một cách nhanh chóng. Nếu tôi thêm tất cả các từ tìm kiếm có thể cùng nhau trong một '(string1 + string2 + ... + stringN) .ToLower(). Chứa()' thì tôi phải nối, xử lý và tìm kiếm thông qua một chuỗi dài hơn nhiều. Tôi nghĩ rằng điều này sẽ chạy chậm hơn (???). – Ozzah

2

Bạn có thể thử thực hiện một hay "tiền tố cây" trie:

http://en.wikipedia.org/wiki/Trie

Điều này sẽ cho phép bạn tìm kiếm văn bản bắt đầu với giá trị.

Tôi tin rằng bài viết được liên kết trên suffix-trees sẽ cho phép bạn thực hiện tìm kiếm văn bản đầy đủ, mặc dù nó có yêu cầu bộ nhớ cao hơn.

Đảm bảo rằng bạn ToLower tất cả dữ liệu của bạn khi chèn nó vào cấu trúc của bạn để bạn không phải thực hiện các so sánh không phân biệt chữ hoa chữ thường khi thực hiện tra cứu.

1

Khi nhìn thấy những gì bạn đã làm (chủ yếu là từ nhận xét về câu trả lời của @ mikel), có vẻ như bạn đang đến đó. Một gợi ý tôi chưa từng thấy có thể làm tăng tốc độ là so sánh sử dụng StringComparison.OrdinalIgnoreCase. Trong trường hợp của bạn, nó sẽ có nghĩa thay thế

if (stf.LastName.ToLower().Contains(s)) 
    return true; 

với

if (stf.LastName.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0) 
    return true; 

Dưới đây là một MSDN link, thảo luận so sánh chuỗi nhanh.

+0

Đây là một ý tưởng thực sự tốt và tôi chắc chắn sẽ sử dụng nó trong tương lai, nhưng đối với trường hợp này tôi nghĩ rằng nó đã lỗi thời kể từ khi tôi đang tính toán trước các phiên bản thấp hơn. Cảm ơn :) – Ozzah

+0

Có vẻ hợp lý, mặc dù bạn nên xem câu hỏi này về trường hợp trên so với trường hợp thấp hơn: http://stackoverflow.com/questions/9033/hidden-features-of-c#12137 –

0
using System; 
using System.Text.RegularExpressions; 
namespace PatternMatching1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       Console.WriteLine("Please enter the first string."); 
       string str = Console.ReadLine(); ; 
       string replacestr = Regex.Replace(str, "[^a-zA-Z0-9_]+", " "); 



       Console.WriteLine("Please enter the second string."); 
       string str1 = Console.ReadLine(); ; 
       string replacestr1 = Regex.Replace(str1, "[^a-zA-Z0-9_]+", " "); 



       if (replacestr.Length == replacestr1.Length) 
       { 
        char[] cFirst = replacestr.ToLower().ToCharArray(); 
        char[] cSecond = replacestr1.ToLower().ToCharArray(); 

        Array.Sort<char>(cFirst); 
        Array.Sort<char>(cSecond); 

        if ((new string(cFirst)).Equals((new string(cSecond)))) 
         Console.WriteLine("Both String Same"); 
        else 
         Console.WriteLine("Both String Not Same"); 
       } 
       else 
        Console.WriteLine("oopsss, something going wrong !!!! try again"); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
      Console.Read(); 
     } 
    } 
} 

'

Các vấn đề liên quan