Как использовать Comparer для HashSet
-
06-07-2019 - |
Вопрос
В результате другого вопроса, который я задал здесь, я хочу использовать HashSet для своих объектов.
Я создам объекты, содержащие строку и ссылку на ее владельца.
public class Synonym
{
private string name;
private Stock owner;
public Stock(string NameSynonym, Stock stock)
{
name=NameSynonym;
owner=stock
}
// [+ 'get' for 'name' and 'owner']
}
Я понимаю, что мне нужен компаратор, но никогда раньше им не пользовался.Стоит ли создавать отдельный класс?нравиться:
public class SynonymComparer : IComparer<Synonym>
{
public int Compare(Synonym One, Synonym Two)
{ // Should I test if 'One == null' or 'Two == null' ????
return String.Compare(One.Name, Two.Name, true); // Caseinsesitive
}
}
Я предпочитаю, чтобы функция (или вложенный класс [может быть, одноэлементный?] если требуется) была ЧАСТЬЮ класса Synonym, а не другого (независимого) класса.Это возможно?
Об использовании:Поскольку я никогда раньше не использовал подобные вещи, я полагаю, что мне нужно написать функцию Find(string NameSynonym) внутри класса Synonym, но как мне это сделать?
public class SynonymManager
{
private HashSet<SynonymComparer<Synonym>> ListOfSynonyms;
public SynonymManager()
{
ListOfSymnonyms = new HashSet<SynonymComparer<Synonym>>();
}
public void SomeFunction()
{ // Just a function to add 2 sysnonyms to 1 stock
Stock stock = GetStock("General Motors");
Synonym otherName = new Synonym("GM", stock);
ListOfSynonyms.Add(otherName);
Synonym otherName = new Synonym("Gen. Motors", stock);
ListOfSynonyms.Add(otherName);
}
public Synonym Find(string NameSynomym)
{
return ListOfSynonyms.??????(NameSynonym);
}
}
В приведенном выше коде я не знаю, как реализовать метод «Найти».Как мне это сделать?
Любая помощь будет оценена (PS Если мои идеи о том, как это должно быть реализовано полностью неверны, дайте мне знать и скажите, как реализовать)
Решение
HashSet не нуждается в IComparer<T>
- для этого нужен IEqualityComparer<T>
, такой как
public class SynonymComparer : IEqualityComparer<Synonym>
{
public bool Equals(Synonym one, Synonym two)
{
// Adjust according to requirements.
return StringComparer.InvariantCultureIgnoreCase
.Equals(one.Name, two.Name);
}
public int GetHashCode(Synonym item)
{
return StringComparer.InvariantCultureIgnoreCase
.GetHashCode(item.Name);
}
}
Однако ваш текущий код компилируется только потому, что вы создаете набор сравнивающие а не набор синонимы.
Более того, я не думаю, что вам вообще нужен набор.Мне кажется, вам нужен словарь или поиск, чтобы найти синонимы для данного имени:
public class SynonymManager
{
private readonly IDictionary<string, Synonym> synonyms = new
Dictionary<string, Synonym>();
private void Add(Synonym synonym)
{
// This will overwrite any existing synonym with the same name.
synonyms[synonym.Name] = synonym;
}
public void SomeFunction()
{
// Just a function to add 2 synonyms to 1 stock.
Stock stock = GetStock("General Motors");
Synonym otherName = new Synonym("GM", stock);
Add(otherName);
ListOfSynonyms.Add(otherName);
otherName = new Synonym("Gen. Motors", stock);
Add(otherName);
}
public Synonym Find(string nameSynonym)
{
// This will throw an exception if you don't have
// a synonym of the right name. Do you want that?
return synonyms[nameSynonym];
}
}
Обратите внимание, что в приведенном выше коде есть несколько вопросов о том, как вы хотите, чтобы он вел себя в различных случаях.Вам нужно потренироваться точно что вы хотите, чтобы он сделал.
РЕДАКТИРОВАТЬ:Если вы хотите иметь возможность хранить несколько акций для одного синонима, вы эффективно хотеть Lookup<string, Stock>
- но это неизменно.Вы, вероятно, лучше всего храните Dictionary<string, List<Stock>>
;список акций для каждой строки.
С точки зрения того, чтобы не выдавать ошибку из Find
, вам стоит посмотреть Dictionary.TryGetValue
который не генерирует исключение, если ключ не найден (а также возвращает, является ли ключ был найденный);сопоставленное значение «возвращается» в выходном параметре.
Другие советы
Не разумнее ли было бы отказаться от Synonym
класс полностью и иметь список синонимов, чтобы быть Dictonary
(или, если такое есть, HashDictionary
) струн?
(Я не очень знаком с типами C#, но надеюсь, что это передает общую идею)
Ответ, который я рекомендую (отредактировано, теперь учитывает случай):
IDictionary<string, Stock>> ListOfSynonyms = new Dictionary<string,Stock>>();
IDictionary<string, string>> ListOfSynForms = new Dictionary<string,string>>();
class Stock
{
...
Stock addSynonym(String syn)
{
ListOfSynForms[syn.ToUpper()] = syn;
return ListOfSynonyms[syn.ToUpper()] = this;
}
Array findSynonyms()
{
return ListOfSynonyms.findKeysFromValue(this).map(x => ListOfSynForms[x]);
}
}
...
GetStock("General Motors").addSynonym('GM').addSynonym('Gen. Motors');
...
try
{
... ListOfSynonyms[synonym].name ...
}
catch (OutOfBounds e)
{
...
}
...
// output everything that is synonymous to GM. This is mix of C# and Python
... GetStock('General Motors').findSynonyms()
// test if there is a synonym
if (input in ListOfSynonyms)
{
...
}
Вы всегда можете использовать LINQ для поиска:
public Synonym Find(string NameSynomym)
{
return ListOfSynonyms.SingleOrDefault(x => x.Name == NameSynomym);
}
Но рассматривали ли вы вместо этого использование словаря? Я считаю, что он лучше подходит для извлечения отдельных членов, и вы все равно можете гарантировать отсутствие дубликатов на основе выбранного вами ключа.
Я не уверен, что время поиска равно SingleOrDefault, но я почти уверен, что оно линейно (O(n)), поэтому, если время поиска важно для вас, словарь предоставит вам время поиска O(1).