Đầu tiên, tôi sẽ thay đổi cách ListControl
xem nguồn dữ liệu của bạn, bạn đang chuyển đổi kết quả IEnumerable<string>
thành List<string>
. Đặc biệt là khi bạn chỉ gõ một vài ký tự, điều này có thể không hiệu quả (và không cần thiết). Không tạo bản sao mở rộng dữ liệu của bạn .
- Tôi sẽ gói
.Where()
kết quả vào một bộ sưu tập chỉ triển khai những gì được yêu cầu từ IList
(tìm kiếm). Điều này sẽ giúp bạn tiết kiệm để tạo một danh sách lớn mới cho mỗi ký tự được nhập.
- Thay vào đó, tôi sẽ tránh LINQ và tôi sẽ viết một cái gì đó cụ thể hơn (và được tối ưu hóa). Giữ danh sách của bạn trong bộ nhớ và xây dựng một mảng các chỉ số phù hợp, sử dụng lại mảng để bạn không phải phân bổ lại nó cho mỗi lần tìm kiếm.
Bước thứ hai là không tìm kiếm trong danh sách lớn khi danh sách nhỏ là đủ. Khi người dùng bắt đầu gõ "ab" và thêm "c" thì bạn không cần phải nghiên cứu trong danh sách lớn, tìm kiếm trong danh sách đã lọc là đủ (và nhanh hơn). Tinh chỉnh tìm kiếm mọi lúc có thể, không thực hiện tìm kiếm đầy đủ mỗi lần.
Bước thứ ba có thể khó hơn: giữ cho dữ liệu có tổ chức để được tìm kiếm nhanh chóng . Bây giờ bạn phải thay đổi cấu trúc bạn sử dụng để lưu trữ dữ liệu của mình. tưởng tượng một cái cây như thế này:
ABC
Thêm Ceil tốt hơn
Đường viền trên xương
Điều này có thể đơn giản được triển khai với một mảng (nếu bạn đang làm việc với các tên ANSI nếu không thì một từ điển sẽ tốt hơn). Tạo danh sách như thế này (mục đích minh họa, nó khớp với phần đầu của chuỗi):
var dictionary = new Dictionary<char, List<string>>();
foreach (var user in users)
{
char letter = user[0];
if (dictionary.Contains(letter))
dictionary[letter].Add(user);
else
{
var newList = new List<string>();
newList.Add(user);
dictionary.Add(letter, newList);
}
}
Sau đó, tìm kiếm sẽ được thực hiện bằng cách sử dụng ký tự đầu tiên:
char letter = textBox_search.Text[0];
if (dictionary.Contains(letter))
{
listBox_choices.DataSource =
new MyListWrapper(dictionary[letter].Where(x => x.Contains(textBox_search.Text)));
}
Xin lưu ý rằng tôi đã sử dụng MyListWrapper()
như đề xuất ở bước đầu tiên (nhưng tôi đã bỏ qua gợi ý thứ 2 cho ngắn gọn, nếu bạn chọn đúng kích thước cho khóa từ điển, bạn có thể giữ cho mỗi danh sách ngắn gọn và nhanh chóng - có thể - tránh bất cứ điều gì khác). Hơn nữa, lưu ý rằng bạn có thể cố gắng sử dụng hai ký tự đầu tiên cho từ điển của mình (nhiều danh sách hơn và ngắn hơn). Nếu bạn mở rộng điều này, bạn sẽ có một cái cây (nhưng tôi không nghĩ rằng bạn có số lượng lớn như vậy).
Có nhiều thuật toán khác nhau để tìm kiếm chuỗi (với các cấu trúc dữ liệu liên quan), chỉ đề cập đến một số:
- Tìm kiếm dựa trên automaton trạng thái hữu hạn : trong cách tiếp cận này, chúng tôi tránh bẻ khóa ngược bằng cách xây dựng một Automaton hữu hạn xác định (DFA) nhận dạng chuỗi tìm kiếm được lưu trữ. Những thứ này rất tốn kém để xây dựng — chúng thường được tạo ra bằng cách sử dụng cấu trúc tập hợp quyền hạn — nhưng rất nhanh chóng để sử dụng.
- Stubs : Knuth – Morris – Pratt tính toán một DFA nhận dạng đầu vào với chuỗi để tìm kiếm dưới dạng hậu tố, Boyer – Moore bắt đầu tìm kiếm từ cuối kim, vì vậy nó thường có thể nhảy lên trước cả chiều dài kim ở mỗi bước. Baeza – Yates theo dõi xem các ký tự j trước đó có phải là tiền tố của chuỗi tìm kiếm hay không và do đó có thể thích ứng với tìm kiếm chuỗi mờ. Thuật toán bitap là một ứng dụng của cách tiếp cận Baeza – Yates.
- Phương pháp lập chỉ mục : các thuật toán tìm kiếm nhanh hơn dựa trên việc xử lý trước văn bản. Sau khi xây dựng một chỉ mục chuỗi con, ví dụ cây hậu tố hoặc mảng hậu tố, có thể nhanh chóng tìm thấy các lần xuất hiện của một mẫu.
- Các biến thể khác : một số phương pháp tìm kiếm, chẳng hạn như tìm kiếm bằng trigram, nhằm mục đích tìm điểm "độ gần gũi" giữa chuỗi tìm kiếm và văn bản hơn là "khớp / không khớp". Đây đôi khi được gọi là tìm kiếm "mờ".
Vài lời về tìm kiếm song song. Điều đó có thể xảy ra nhưng nó hiếm khi nhỏ vì chi phí để làm cho nó song song có thể dễ dàng cao hơn nhiều so với bản thân việc tìm kiếm. Tôi sẽ không thực hiện tìm kiếm song song (phân vùng và đồng bộ hóa sẽ sớm trở nên quá rộng và có thể phức tạp) nhưng tôi sẽ chuyển tìm kiếm sang một chuỗi riêng biệt . Nếu luồng chính không bận, người dùng của bạn sẽ không cảm thấy bất kỳ sự chậm trễ nào khi họ đang nhập (họ sẽ không lưu ý liệu danh sách có xuất hiện sau 200 mili giây hay không nhưng họ sẽ cảm thấy khó chịu nếu phải đợi 50 mili giây sau khi nhập) . Tất nhiên bản thân tìm kiếm phải đủ nhanh, trong trường hợp này, bạn không sử dụng các chuỗi để tăng tốc tìm kiếm mà để giữ cho giao diện người dùng của bạn phản hồi . Xin lưu ý rằng một chuỗi riêng biệt sẽ không thực hiện truy vấn của bạnnhanh hơn , nó sẽ không treo giao diện người dùng nhưng nếu truy vấn của bạn chậm, nó sẽ vẫn chậm trong một chuỗi riêng (hơn nữa bạn cũng phải xử lý nhiều yêu cầu tuần tự).
HashSet<T>
sẽ không giúp bạn ở đây, vì bạn đang tìm kiếm một phần của chuỗi.