SortedList не удаляет и не добавляет элементы правильно

Я столкнулся с проблемой SortedList, когда 2 метода дают 2 разных результата.

//Item Data is one of this sortedList item
var itemPos = Items.IndexOfValue(ItemData);
Item item;
Items.TryGetValue(itemPos, out item);

Результат не так очевиден. Я буду оперировать цифрами, а не абстрактными буквами, чтобы лучше проиллюстрировать происходящее.

itemPos устанавливается на 5. Хорошо! Затем мы пытаемся снова получить этот элемент из этого индекса, но нет. Он возвращает ноль. Конечно, это происходит не сразу. Этот код вызывается до того, как это произойдет.

    public void MoveItem(int indexFrom, int indexWhere)
    {
        Item itemToSawp;
        Items.TryGetValue(indexFrom, out itemToSawp);
        Items.Remove(indexFrom);
        Items.Add(indexWhere, itemToSawp);
    }

Чтобы переместить элементы в отсортированном списке, мы должны удалить и снова добавить элемент. В порядке! Отладка говорит, что операция прошла замечательно, и мой элемент теперь имеет индекс 5, я переместил его из индекса 4. Там, где индекс 5 был пустым до метода MoveItem.

Или он был пуст? Перед этой операцией у меня был индекс 5, заполненный вещами, и я вызвал простые Items.Remove(5);

Но теперь происходит то, что я описал ранее.

Стоит отметить, что это происходит только тогда, когда я перемещаю элемент вверх по индексу, смотрите с 4 по 5. При переходе с 5-4 все работает корректно.

Ты хоть представляешь, что здесь происходит? Я использую .NET 3.5

Полный код

using System;
using System.Collections.Generic;

class Program
{
    static SortedList<int, ItemData> Items = new SortedList<int, ItemData>();

    static void Main(string[] args)
    {
        var Foo = new ItemData();
        Items.Add(0, Foo);
        Items.Add(1, new ItemData());
        Items.Remove(1);
        MoveItem(0, 1);
        var itemPos = Items.IndexOfValue(Foo);
        Console.WriteLine(itemPos);
        //Console should return 1 i think
        ItemData item;
        Items.TryGetValue(itemPos, out item);
    }

    public static void MoveItem(int indexFrom, int indexWhere)
    {
        ItemData itemToSawp;
        Items.TryGetValue(indexFrom, out itemToSawp);
        Items.Remove(indexFrom);
        Items.Add(indexWhere, itemToSawp);
    }

    class ItemData
    {

    }
}

РЕДАКТИРОВАТЬ: Это немного сбивает с толку, но! Индексатор (посмотрите на его имя: P) Возьмите в качестве аргумента KEY, а не INDEX. Это то, что меня смутило, и я все перепутал. Как сказал Кристоф. Лучше использовать свой собственный список, который вы можете использовать по желанию или внимательно прочитать документацию.


person Tomasz Juszczak    schedule 10.09.2015    source источник
comment
Было бы легче понять проблему, если бы вы могли просто предоставить короткую, но полную программу, демонстрирующую проблему, а не описывающую биты.   -  person Jon Skeet    schedule 10.09.2015
comment
Конечно, но проблема кажется более сложной, чем это. Я описал это здесь несколько в общих чертах, что происходит в моем коде, и я надеялся, что кто-то здесь тоже это обнаружит, и у него есть решение.   -  person Tomasz Juszczak    schedule 11.09.2015
comment
К сожалению, описание в общем виде не поможет нам воспроизвести его, тогда как короткий, но полный пример поможет. Ваш текущий полный код начинается с объявления метода или использования директив — когда я говорю короткий, но полный пример, я имею в виду то, что я могу скопировать, вставить, скомпилировать и запустить. Мы можем сделать это из этого, но нет необходимости, чтобы каждый, кто хочет помочь вам, прилагал дополнительные усилия, когда вы могли бы сделать это с самого начала.   -  person Jon Skeet    schedule 11.09.2015
comment
Извини. Я забыл вставить несколько важных, но легко исправимых строк. Буду помнить на будущее.   -  person Tomasz Juszczak    schedule 11.09.2015


Ответы (2)


Насколько я могу судить, вы неправильно понимаете концепцию SortedList. SortedLists используются, когда у вас есть объекты, связанные с ключами, и ключи можно сортировать, а их порядок важен для производительности, определенного алгоритма или чего-то подобного. Например, подумайте о марафоне, вы можете хранить объекты Runner в зависимости от времени их финиша в SortedList. В любом случае имейте в виду, что ключ является сортируемым значением, и каждый ключ связан с произвольным объектом значения.

Теперь некоторые проблемы, которые я наблюдаю в вашем коде:

  • В первом поле кода, строка 2, вы найдете индекс объекта-значения. Это противоречит цели использования SortedList, потому что эта операция выполняется медленно, тогда как поиск значения с использованием ключа выполняется быстро (через внутреннюю хэш-таблицу или что-то в этом роде).
  • В первом поле кода, строка 4, вы вызываете TryGetValue. Посмотрите определение, первый параметр - это ключ, а не индекс в SortedList. Так что этот пример неверен с семантической точки зрения.

Что касается перемещения элементов в SortedList (поле кода 2), всегда потребуется удалить объект значения с использованием исходного ключа, а затем добавить объект значения с другим ключом (обычно большим или меньшим). Но опять же, я не понимаю, почему вы все равно хотите перемещать элементы в SortedList. Все дело в том, что вы можете просто добавлять объекты-значения, связанные с сортируемым ключом, и SortedList автоматически сортирует все эти объекты за вас.

У меня есть ощущение, что вы, возможно, захотите рассмотреть обычный объект List или даже просто массив, если размер фиксирован или ограничен. Затем вы получаете всю свою семантику индекса и можете менять местами элементы, если это действительно то, что ваш алгоритм хочет сделать.

Редактировать: я только что видел полный код. Мои общие рекомендации выше стоят. Проблема в вашем полном примере заключается в том, что вы путаете ключи с индексами. После MoveItem(0,1) объект Foo регистрируется с ключом 1, но поскольку в SortedList есть только одна запись, она имеет индекс 0, который вы получаете с помощью IndexOfValue (медленная операция). Затем, когда вы делаете TryGetValue, вы действительно ищете запись с ключом 0, которого не существует. Вы неправильно предположили, что TryGetValue будет принимать индекс в качестве параметра.

person Christoph    schedule 10.09.2015
comment
Я использовал отсортированный список, чтобы иметь индексы и всегда сортировать их. Отсортированный список также сортирует элементы, если индекс не существует и может расширяться. Вот почему я использовал его в своем проекте. Я могу изменить это, но я хотел бы знать, почему такие операции не работают. - person Tomasz Juszczak; 11.09.2015
comment
Просто добавил к моему ответу после редактирования:... - person Christoph; 11.09.2015
comment
Ой. Ты прав! Я упустил из виду функциональность SortedList! Как вы сказали, я попытаюсь реализовать свой собственный список, если он будет выглядеть так. - person Tomasz Juszczak; 11.09.2015

Вы путаетесь между ключом записи и индексом записи. Ваш метод MoveItem просто изменяет ключ, связанный со значением (путем удаления старой записи и создания новой записи). После этих строк:

Items.Add(0, Foo);
Items.Add(1, new ItemData());
Items.Remove(1);

... в коллекции будет только одна запись, и MoveItem будет удалять/добавлять, чтобы не изменить количество. Поэтому IndexOfValue может только вернуть 0 (или -1, если он не найден).

Чтобы получить 1, нужно найти ключ, связанный со значением, а не индекс. Например:

int index = Items.IndexOfValue(Foo);
int key = Items.Keys[index];
Console.WriteLine("Key = {0}", key); // Prints 1

Обратите внимание, что TryGetValue принимает ключ, а не индекс, поэтому эта строка:

Items.TryGetValue(itemPos, out item);

... было бы очень странно.

Все это легче увидеть, если вы используете другой тип ключа, например. строка. Тогда вы не сможете запутаться между ключами и индексами, потому что типы разные и компилятор не позволит вам использовать один там, где вы имели в виду другой.

person Jon Skeet    schedule 10.09.2015
comment
Да! Это верно, теперь я понял, что происходит, но Кристоф был быстрее, в любом случае, есть Up. - person Tomasz Juszczak; 11.09.2015