Или баг в моих мозгах...Ничего не понимаю.
Есть SortedSet<Tuple<string, int>>
Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост,
код компаратора даже нету смысла приводить:
если строки равны возвращаем 0.
иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2)
Но, как же я удивился как например после того как в списке есть ("10", 8)
то ("10", 0) все равно добавляется, несмотря на обещанное
"Класс SortedSet<T>не принимает повторяющихся элементов. Если item уже присутствует в наборе, то этот метод возвращает значение false и не вызывает исключение"
Такой код:
SortedSet<Tuple<string, int>> l = new SortedSet<Tuple<string, int>>(new MyComparer());
for (int i = 0; i < 100; i++)
l.Add(new Tuple<string, int>(i.ToString(), i));
for (int i = 0; i < 10; i++)
l.Add(new Tuple<string, int>(i.ToString(), default(int)));
Console.WriteLine(l.Count);
выводит 106, хотя я ожидаю 100
В то же время
SortedSet<Tuple<string, int>> l = new SortedSet<Tuple<string, int>>(new MyComparer());
for (int i = 0; i < 100; i++)
l.Add(new Tuple<string, int>(i.ToString(), i));
for (int i = 0; i < 10; i++)
l.Remove(new Tuple<string, int>(i.ToString(), default(int)));
Console.WriteLine(l.Count);
успешно удаляет 10 элементов и выводит 90
Т.е. при удалении элементы равны, а при добавлении и Contains — нет
Что за двойные стандарты
Здравствуйте, g-host, Вы писали:
GH>Или баг в моих мозгах...Ничего не понимаю. GH>Есть SortedSet<Tuple<string, int>> GH>Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост, GH>код компаратора даже нету смысла приводить: GH>если строки равны возвращаем 0. GH>иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2) GH>Но, как же я удивился как например после того как в списке есть ("10", 8) GH>то ("10", 0) все равно добавляется, несмотря на обещанное
Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.
Здравствуйте, samius, Вы писали:
S>Здравствуйте, g-host, Вы писали:
S>Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.
Так оно и есть.
Но проблема в том что только такая реализация может обеспечить нужное мне поведение.
Да и смущает "правильная" работа Remove, ведь в нем также используется не линейный поиск.
Есть какие-либо альтернативы? Елементов на данный момент планируется хранить
порядка нескольких десятков тысяч. Я понимаю что это может быть даже не очень много, но все же
хотелось бы ускорить работу с коллекцией при таком алгоритме сравнения.
Здравствуйте, g-host, Вы писали:
GH>Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост, GH>код компаратора даже нету смысла приводить: GH>если строки равны возвращаем 0. GH>иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2)
Если я все правильно понял, то ваша реализация компарера некорректна. Компарер задает упорядочение элементов, при этом всегда должно выполняться
(t1 < t2) && (t2 < t3) => t1 < t3
Предложенная вами реализация не соблюдает этот инвариант:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
[STAThread]
static void Main()
{
var cmp = new MyComparer();
var t1 = Tuple.Create("A", 1);
var t2 = Tuple.Create("B", 2);
var t3 = Tuple.Create("A", 3);
Console.WriteLine("t1 < t2: {0}", cmp.Compare(t1, t2) < 0);
Console.WriteLine("t2 < t3: {0}", cmp.Compare(t2, t3) < 0);
Console.WriteLine("t1 < t3: {0}", cmp.Compare(t1, t3) < 0);
}
}
internal class MyComparer : IComparer<Tuple<string, int>>
{
public int Compare(Tuple<string, int> x, Tuple<string, int> y)
{
if (string.Equals(x.Item1, y.Item1))
return 0;
if (x.Item2 != y.Item2)
return Comparer<int>.Default.Compare(x.Item2, y.Item2);
return string.Compare(x.Item1, y.Item1);
}
}
}
Мне лень проверять деуствительно ли это причина, но я практически уверен, что это она и есть.
Здравствуйте, g-host, Вы писали:
S>>Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.
GH>Так оно и есть. GH>Но проблема в том что только такая реализация может обеспечить нужное мне поведение.
Вы уверены, что вам нужен именно SortedSet? Ведь ему по определению нужен способ упорядочения, а вы его предложить не можете.
Может вам больше подойдет HashSet?
Здравствуйте, g-host, Вы писали:
GH>Здравствуйте, samius, Вы писали:
S>>Здравствуйте, g-host, Вы писали:
S>>Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.
GH>Так оно и есть. GH>Но проблема в том что только такая реализация может обеспечить нужное мне поведение.
Но такая реализация не может обеспечить корректной работы SortedSet GH>Да и смущает "правильная" работа Remove, ведь в нем также используется не линейный поиск.
Стоит удалить 10 случайных элементов и наверняка будет различное поведение при повторных запусках.
GH>Есть какие-либо альтернативы? Елементов на данный момент планируется хранить GH>порядка нескольких десятков тысяч. Я понимаю что это может быть даже не очень много, но все же GH>хотелось бы ускорить работу с коллекцией при таком алгоритме сравнения.
Set<T> не подойдет?
Если нужен порядок, то требуется корректное отношение порядка. Хитрить тут можно только в рамках соблюдения свойств отношения полного порядка.
Всем спасибо, свою ошибку насчет компаратора осознал
Для Eduals и GetHashCode в случае HashSet сходу реализацию не придумал,
слишком много требований должно выполняться — кроме того что список надо хранить
в отсортированном порядке для ускорения доступа, происходит периодическая подгрузка данных из внешнего источника,
и при этом с этими данными постоянно работает "клиентский" код, который также должен рапортовать
об актуальности полученных данных, опять же влияя тем самым на порядок элементов.
В любом случае, в контексте этого топика это уже оффтоп и отход от темы.
Re: в SortedSet<T> баг?
От:
Аноним
Дата:
17.05.11 13:24
Оценка:
Здравствуйте, g-host, Вы писали:
GH>Или баг в моих мозгах...Ничего не понимаю. GH>Есть SortedSet<Tuple<string, int>> GH>Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост, GH>код компаратора даже нету смысла приводить: GH>если строки равны возвращаем 0. GH>иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2) GH>Но, как же я удивился как например после того как в списке есть ("10", 8) GH>то ("10", 0) все равно добавляется, несмотря на обещанное GH>"Класс SortedSet<T>не принимает повторяющихся элементов. Если item уже присутствует в наборе, то этот метод возвращает значение false и не вызывает исключение"
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, g-host, Вы писали:
А>SortedDictionary или SortedList не подойдёт?
Да, я на них уже смотрел, не помню почему именно но не подошли.
Проблема так-то уже давно решена с помощью обычного List<T>
используется сортировка вставками(с помощью BinarySearch с нужным компаратором и последующего Insert),
и иногда быстрая сортировка(при подгрузке данных, писал выше). Удалось добиться приемлемой скорости.