#c# #list #dictionary #indexed
#c# #Список #словарь #индексированный
Вопрос:
Я пытаюсь написать программу, в которой словарь индексируется списком. (поверьте мне, я делаю, и да, есть вариант, но мне нравится индексирование по списку). Существует минимальный рабочий (на самом деле не работающий, только одна последняя строка, которая является проблемой) пример:
using System;
using System.Collections.Generic;
namespace test
{
class Program
{
static void Main(string[] args)
{
Dictionary<List<String>, int> h = new Dictionary<List<string>,int>();
List<String> w = new List<string> {"a"};
h.Add(w, 1);
w = new List<string>{"b"};
h.Add(w,2);
w = new List<string>{"a"};
int value = 0;
h.TryGetValue(w, out value);
Console.WriteLine(value " " h[w]);
}
}
если кто-то отладит эту программу, он ясно увидит, что в h есть два элемента, но все равно эти элементы недоступны через правильные индексы — h [w]. Я ошибаюсь или происходит что-то странное?
Комментарии:
1. Класс list, вероятно, не производит хэш-код и проверку на равенство на основе его содержимого. Вы проверили, работают ли массивы лучше?
2. Или, может быть, использовать пользовательский класс или
Tuple<T, T2....>
3. Нет, массивы ведут себя точно так же.
4. Вероятно, вам понадобится пользовательский IEqualityComparer
5. @LasseV.Karlsen: Они не будут: ideone.com/7Gvvh
Ответ №1:
Проблема с вашим приложением связана с тем, что:
new List<String> { "a" } != new List<String> { "a" }
Равенство для списков проверяет, ссылаются ли две ссылки на один и тот же экземпляр. В данном случае это не так. Вместо этого вы создали два списка с одинаковыми элементами…что не делает их равными.
Вы можете устранить проблему, создав пользовательский инструмент сравнения равенства:
public class ListEqualityComparer<T> : IEqualityComparer<List<T>>
{
public bool Equals(List<T> list1, List<T> list2)
{
return list1.SequenceEquals(list2);
}
public int GetHashCode(List<T> list)
{
if(list != null amp;amp; list.Length > 0)
{
var hashcode = list[0].GetHashCode();
for(var i = 1; i <= list.Length; i )
hashcode ^= list[i].GetHashCode();
return hashcode;
}
return 0;
}
}
И затем передаем это конструктору Dictionary:
Dictionary<List<String>, int> h =
new Dictionary<List<string>,int>(new ListEqualityComparer<String>());
Комментарии:
1. Обычно при сравнении двух ссылочных типов сравнение возвращает true, если обе ссылки указывают на один и тот же объект. Если объекты разные, то даже их содержимое одинаковое, результат будет false.
2. Другим вариантом было бы использование
ToArray()
иIStructuralEquatable.GetHashCode
для более простой (хотя и медленной) реализации. (Или, может быть, с использованием массивов иDictionary<IStructuralEquatable, int>
для начала.)3. Спасибо! работает как шарм, это всего лишь
for(var i = 1; i < list.Length; i )
Ответ №2:
Проблема заключается в индексации по списку, то, что вы индексируете, не является данными в списке, но вы, по сути, индексируете по указателю памяти на список (т. е. адресу памяти, где находится этот список).
Вы создали один список в одной ячейке памяти, затем вы создали совершенно другой список в другой ячейке памяти (т. Е. при создании нового экземпляра). Два списка отличаются, даже если они содержат одинаковые данные, и это означает, что вы можете добавить в словарь столько, сколько захотите.
Одним из решений вместо индексации по списку было бы индексирование по строке и использование списка, разделенного запятыми, содержащего все данные в вашем списке, в качестве индекса.
Комментарии:
1. Технически это не указатель, но это правильно. Список<T> не вычисляет хэш-коды на основе содержимого: ideone.com/AEIDO
Ответ №3:
У вас это никогда не сработает, потому что List<T>
методы Equals
и GetHashCode
не учитывают содержимое списка. Если вы хотите использовать коллекцию объектов в качестве ключа, вам нужно реализовать свой собственный тип коллекции, который переопределяет Equals
таким образом, чтобы проверять равенство объектов в коллекции (возможно, используя Enumerable.SequenceEqual
.)
Комментарии:
1. О, или вы можете использовать
IEqualityComparer
, как упомянутый выше парень в комментарии, но я оставлю это кому-нибудь другому.
Ответ №4:
Класс Dictionary использует сравнение ссылок для поиска указанного ключа, поэтому, даже если списки содержат одинаковые элементы, они разные.
Комментарии:
1. в этом конкретном контексте . В общем, это не так, но со списком, который не переопределяет GetHashCode или Equals, это так.