Использование хэш-таблиц/словарей со строковыми ключами и поиском без учета регистра

Интересно, возможно ли это.

У нас есть сторонняя библиотека, которая содержит идентификационную информацию о пользователях...

Основное взаимодействие с библиотекой осуществляется через хэш-таблицу, которая имеет ключ со строкой и возвращает граф объектов с информацией для этого ключа.

Проблема в том, что ключ, очевидно, чувствителен к регистру, но то, что мы получаем от браузера пользователей, не обязательно соответствует регистру... (Мы часто получаем ключ полностью в нижнем регистре)

Мне интересно, возможно ли выполнить поиск нечувствительного ключа к регистру по хеш-таблице.

e.g.

Hashtable ht = new Hashtable();
ht.Add("MyKey", "Details");

string result = ht["MyKey"];
string result = ht["MYKEY"];
string result = ht["mykey"];

На всякий случай мы могли бы отправить запрос в службу поддержки компании, чтобы добавить эту функциональность, существуют ли какие-либо другие структуры данных (т. е. новые общие коллекции/словари), которые поддерживают эту функциональность.

Наконец, можно ли переопределить метод System.String GetHashCode(), чтобы все строки, инвариантные к регистру, возвращали один и тот же хэш-код... например. Я думаю, что это не годится, так как string - это закрытый класс

Приветствую, если у кого-то есть какие-либо предложения


person Eoin Campbell    schedule 13.05.2009    source источник
comment
в стороне: если вы используете .NET 3.5, вам, вероятно, следует прекратить использовать Hashtable и т. д. - и вместо этого начать использовать общие коллекции.   -  person Marc Gravell    schedule 13.05.2009
comment
По поводу вашего комментария: тогда, может быть, просто вызовите .ToLowerInvariant() для всех ваших ключей.   -  person Marc Gravell    schedule 13.05.2009
comment
чешет голову Но ключи внутри Hashtable не ToLowered(). т.е. Мне дали предварительно созданную хеш-таблицу, которая создается/заполняется внутри dll. а ключи в HT чувствительны к регистру ... Единственный доступ, который у меня есть к dll, - это через геттер/индексатор, где я передаю ключ, поэтому, даже если я toLowerInvarianted мои ключи снаружи, они не будут соответствовать ключам на внутренней.   -  person Eoin Campbell    schedule 13.05.2009


Ответы (4)


Код для сравнения хэш-таблиц без учета регистра.

Для 2.0, 3.0, 3.5

Hashtable ht = new Hashtable(StringComparer.InvariantCultureIgnoreCase);

Вы можете получить информацию о InvariantCultureIgnoreCase и OrdinalIgnoreCase по этой ссылке SO

OR

Hashtable ht = System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable();

Поскольку коллекция словарей без учета регистра используется очень часто, в .NET Framework есть класс CollectionUtil, который поддерживает создание объектов Hashtable и SortedList, нечувствительных к регистру. Используйте, вызвав CreateCaseInsensitiveHashtable или CreateCaseInsensitiveSortedList.

Для .Net 1.0 (я не уверен, поддерживает ли 1.0 StringComparer)

public class InsensitiveComparer : IEqualityComparer
{
    CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();
    public int GetHashCode(object obj)
    {
        return obj.ToString().ToLowerInvariant().GetHashCode();
    }

    public new bool Equals(object x, object y)
    {
        if (_comparer.Compare(x, y) == 0)
        {
            return true;
        }

        else
       {
           return false;
       }
    }
}

Hashtable dehash = new Hashtable(new InsensitiveComparer());
person SO User    schedule 13.05.2009
comment
Класс StringComparer уже предоставляет компараторы без учета регистра — нет необходимости реализовывать свои собственные. - person Daniel Brückner; 13.05.2009
comment
Это просто к вашему сведению: я обнаружил, что ключи StringDictionary по умолчанию нечувствительны к регистру. Но и ключ, и значение должны быть строками. - person SO User; 15.05.2009
comment
Также вы можете использовать CreateCaseInsensitiveHashtable. Отредактировал мой ответ, чтобы отразить использование. - person SO User; 15.05.2009

Со словарем:

new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

но проще, я считаю, что StringDictionary также нечувствителен к регистру:

    StringDictionary ht = new StringDictionary();
    ht.Add("MyKey", "Details");

    string result1 = ht["MyKey"];
    string result2 = ht["MYKEY"];
    string result3 = ht["mykey"];
person Marc Gravell    schedule 13.05.2009
comment
Здоровья Марк. И приветствую других за указание на перегрузки IEqualityOperator... К сожалению, у меня нет доступа к внутренностям библиотеки или конструкции Hashtable... Я обнаружил реализацию только с помощью Reflector, чтобы поковыряться внутри нее. Я думаю, что я зарегистрирую билет поддержки в компанию и спрошу их, можно ли добавить его в качестве функции. - person Eoin Campbell; 13.05.2009

Вы можете предоставить нечувствительный к регистру IEqualityComparer конструктору HashTable, но для этого необходимо, чтобы вы могли влиять на конструкцию HashTable в сторонней библиотеке.

Если вы заполните хеш-таблицу из своего кода, вы можете нормализовать ключи при вставке и снова получить нормализованные ключи.

Если вы не можете повлиять на содержимое хэш-таблицы, но знаете структуру ключей, вы можете исправить ввод пользователя перед доступом к хеш-таблице.

person Daniel Brückner    schedule 13.05.2009

Это не очень эффективный способ, но вы всегда можете взять хеш-таблицу и сделать из нее StringDictionary:

Hashtable ht = new Hashtable();
ht.Add("FOO", "bar");

StringDictionary dict = new StringDictionary();

foreach(string key in ht.Keys)
    dict.Add(key, ht[key].ToString());

string result = dict["foo"]; // Assigns "bar" to result
person Rytmis    schedule 13.05.2009
comment
+1 Это хорошая идея. (Но не работает с FOO и foo в хеш-таблице.) - person Daniel Brückner; 13.05.2009
comment
Ну, использование индексатора вместо метода Add предотвратило бы исключение, и не будет ли любое решение иметь ту же проблему, если исходная Hashtable имеет несколько ключей, которые различаются только регистром? - person Rytmis; 13.05.2009