2013-06-04 18 views
36

Tôi có phương pháp với tham số HashSet. Và tôi cần phải làm các trường hợp không phân biệt chữ hoa chữ thường trong đó:Đặt HashSet <string> không phân biệt chữ hoa chữ thường

public void DoSomething(HashSet<string> set, string item) 
{ 
    var x = set.Contains(item); 
    ... 
} 

Có cách nào để làm cho HashSet không phân biệt chữ hoa chữ thường (không tạo mới)?

Tôi đang tìm giải pháp có hiệu quả tốt nhất.

Sửa

Chứa có thể được gọi nhiều lần. Vì vậy, các phần mở rộng của IEnumerable không được chấp nhận đối với tôi do hiệu suất thấp hơn so với phương thức HashSet Contains gốc.

Giải pháp

Kể từ khi, câu trả lời cho câu hỏi của tôi là NO, nó là không thể, tôi đã tạo ra và sử dụng phương pháp sau đây:

public HashSet<string> EnsureCaseInsensitive(HashSet<string> set) 
{ 
    return set.Comparer == StringComparer.OrdinalIgnoreCase 
      ? set 
      : new HashSet<string>(set, StringComparer.OrdinalIgnoreCase); 
} 
+5

Có thể bạn sẽ phải tạo một cái mới ... –

+0

Có thể trùng lặp: http://stackoverflow.com/questions/2667635/how-to-use-hashsetstring-contains-method-in-case-insensitive- mode (xem câu trả lời của user414076) –

+0

Bạn cần quyết định xem có phải trường hợp xem xét 'HashSet' hay không bằng cách cung cấp một trình so sánh. Tuy nhiên, đáng xem xét rằng tập hợp {"A", "a"} sẽ chỉ chứa một mục có bộ so sánh phân biệt chữ hoa chữ thường. – spender

Trả lời

67

Các constructor HashSet<T> có tình trạng quá tải cho phép bạn vượt qua trong một tùy chỉnh IEqualityComparer<string>. Có một vài trong số này được xác định cho bạn đã có trong lớp tĩnh StringComparer, một vài trong số đó bỏ qua trường hợp. Ví dụ:

var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase); 
set.Add("john"); 
Debug.Assert(set.Contains("JohN")); 

Bạn sẽ phải thực hiện thay đổi này tại thời điểm xây dựng HashSet<T>. Khi đã tồn tại, bạn không thể thay đổi số IEqualityComparer<T> đang sử dụng.


Chỉ cần để bạn biết, theo mặc định (nếu bạn không vượt qua trong bất kỳ IEqualityComparer<T> để các nhà xây dựng HashSet<T>), nó sử dụng EqualityComparer<T>.Default để thay thế.


Sửa

Câu hỏi đặt ra dường như đã thay đổi sau khi tôi đăng câu trả lời của tôi. Nếu bạn phải làm một trường hợp không nhạy cảm tìm kiếm trong một trường hợp hiện nhạy cảmHashSet<string>, bạn sẽ phải thực hiện tìm kiếm tuyến tính:

set.Any(s => string.Equals(s, item, StringComparison.OrdinalIgnoreCase)); 

Không có cách nào xung quanh này.

+0

Nếu bạn đang thực hiện tra cứu đơn lẻ - điều này tệ hơn là chỉ lặp qua hashset –

+0

@DaveBish Tôi tin rằng OP đã thay đổi câu hỏi của mình để nói "không tạo câu hỏi mới" sau khi tôi đã trả lời ... (chỉnh sửa rất sớm sau khi đăng không thực sự được tính là chỉnh sửa). - Nếu OP phải làm điều này với * một hiện tại * 'HashSet ', thì tất nhiên anh ta sẽ phải thực hiện tìm kiếm thời gian tuyến tính. –

+1

Đó không phải là những gì tôi đang nói. Nếu anh ta chỉ thực hiện một tra cứu đối với hashset - việc tạo một cái mới sẽ tốn kém hơn một lần quét tuyến tính. (Op không chỉ định) –

3

Giả sử bạn đã có phương pháp mở rộng này:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) 
{ 
    return new HashSet<T>(source); 
} 

Bạn chỉ có thể sử dụng quyền này:

set = set.Select(n => n.ToLowerInvariant()).ToHashSet(); 

Hoặc, bạn chỉ có thể làm điều này:

set = new HashSet(set, StringComparer.OrdinalIgnoreCase); 
//or InvariantCultureIgnoreCase or CurrentCultureIgnoreCase 
+1

Nếu bạn đang thực hiện một tra cứu đơn lẻ - điều này tệ hơn là chỉ lặp lại trên hashset –

+0

@DaveBish Tại sao lại như vậy? –

+0

Nó sẽ lấy rất nhiều bộ nhớ và làm rất nhiều tính toán băm, sau đó ném tất cả những công việc đi sau một tra cứu. Vòng lặp trên toàn bộ bộ băm và so sánh phân biệt chữ hoa chữ thường chạy trong bộ nhớ không đổi và không phải tính băm. Cả hai cần phải chạm vào toàn bộ 'set' trong mọi trường hợp. – delnan

0

Nếu bạn muốn rời khỏi, case-sensitive phiên bản gốc tại chỗ, bạn chỉ có thể truy vấn nó với LINQ với trường hợp vô hồn:

var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase)); 
1

Phương thức khởi tạo của HashSet có thể thay thế IEqualityComparer có thể ghi đè cách xác định bình đẳng. Xem danh sách các nhà xây dựng here.

Lớp StringComparer chứa một loạt các phiên bản tĩnh của IEqualityComparers cho chuỗi. Đặc biệt, bạn có thể quan tâm đến StringComparer.OrdinalIgnoreCase. Here là tài liệu của StringComparer.

Lưu ý rằng một hàm tạo khác có một số IEnumerable, vì vậy bạn có thể tạo HashSet mới từ số cũ của mình, nhưng với số IEqualityComparer.

Vì vậy, tất cả cùng nhau, bạn muốn chuyển đổi HashSet của bạn như sau:

var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase); 
5

Bạn không thể tạo ra một cách kỳ diệu HashSet (hoặc từ điển) để hoạt động theo cách phân biệt chữ hoa chữ thường.

Bạn phải tạo lại một bên trong hàm của mình nếu bạn không thể dựa vào số HashSet đến không phân biệt chữ hoa chữ thường.

Hầu hết các mã nhỏ gọn - sử dụng constructor từ bộ hiện có:

var insensitive = new HashSet<string>(
    set, StringComparison.InvariantCultureIgnoreCase); 

Lưu ý rằng sao chép HashSet là đắt tiền như đi qua tất cả các mục, vì vậy nếu chức năng của bạn không chỉ trên tìm kiếm nó sẽ rẻ hơn (O (n)) để lặp qua tất cả các mục. Nếu hàm của bạn được gọi nhiều lần để thực hiện tìm kiếm không phân biệt chữ hoa chữ thường, bạn nên cố gắng chuyển đúng số HashSet cho nó.

+0

+1 cho ghi chú hiệu suất – wishmaster

4

HashSet được thiết kế để nhanh chóng tìm các phần tử theo hàm băm của nó và bộ so sánh bình đẳng. Những gì bạn đang yêu cầu thực sự là tìm một yếu tố phù hợp với điều kiện "khác". Hãy tưởng tượng rằng bạn có một đối tượng Set<Person> chỉ sử dụng Person.Name để so sánh và bạn cần tìm một phần tử với một số giá trị nhất định là Person.Age.

Vấn đề là bạn cần phải lặp qua nội dung của bộ này để tìm các phần tử phù hợp. Nếu bạn định làm điều này thường xuyên, bạn có thể tạo một Bộ khác, trong trường hợp bạn sử dụng bộ so sánh phân biệt chữ hoa chữ thường nhưng bạn phải đảm bảo rằng bộ bóng này được đồng bộ với bản gốc.

Các câu trả lời cho đến nay về cơ bản là các biến thể của ở trên, tôi đã nghĩ để thêm điều này để làm rõ vấn đề cơ bản.

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