Подтвердить что ты не робот

Почему в .NET нет SortedList <T>?

Почему существует только SortedList<TKey, TValue>, который больше похож на словарь, но не SortedList<T>, который на самом деле является только списком, который всегда сортируется?

В соответствии с документацией MSDN на SortedList, она фактически внутренне реализована как массив с динамическим размером KeyValuePair<TKey, TValue>, который всегда сортируется по ключ. Не мог бы тот же класс быть более полезным в качестве списка любого типа T? Разве это не соответствовало бы названию лучше?

4b9b3361

Ответ 1

Хотя никто не может действительно сказать вам, почему нет SortedList<T>, можно обсудить, почему SortedList берет ключ и значение. Словарь сопоставляет ключи со значениями. Типичные способы сделать это - это бинарное дерево, хеш-таблица и список (массив), хотя хэш-таблицы наиболее распространены, поскольку для большинства операций они являются O (1).

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

Если вы решите реализовать карту в виде списка, вы сохраните элементы, отсортированные по ключу, чтобы поиск был O (lg n), предоставив вам другой отсортированный словарь - в виде отсортированного списка. Конечно, имя SortedDictionary уже было принято, но SortedList не было. Я мог бы назвать его SortedListDictionary или SortedDictionaryList, но я не стал его называть.

Ответ 2

Теперь есть:)

public class SortedList<T> : ICollection<T>
{
    private List<T> m_innerList;
    private Comparer<T> m_comparer;

    public SortedList() : this(Comparer<T>.Default)
    {
    }

    public SortedList(Comparer<T> comparer)
    {
        m_innerList = new List<T>();
        m_comparer = comparer;
    }

    public void Add(T item)
    {
        int insertIndex = FindIndexForSortedInsert(m_innerList, m_comparer, item);
        m_innerList.Insert(insertIndex, item);
    }

    public bool Contains(T item)
    {
        return IndexOf(item) != -1;
    }

    /// <summary>
    /// Searches for the specified object and returns the zero-based index of the first occurrence within the entire SortedList<T>
    /// </summary>
    public int IndexOf(T item)
    {
        int insertIndex = FindIndexForSortedInsert(m_innerList, m_comparer, item);
        if (insertIndex == m_innerList.Count)
        {
            return -1;
        }
        if (m_comparer.Compare(item, m_innerList[insertIndex]) == 0)
        {
            int index = insertIndex;
            while (index > 0 && m_comparer.Compare(item, m_innerList[index - 1]) == 0)
            {
                index--;
            }
            return index;
        }
        return -1;
    }

    public bool Remove(T item)
    {
        int index = IndexOf(item);
        if (index >= 0)
        {
            m_innerList.RemoveAt(index);
            return true;
        }
        return false;
    }

    public void RemoveAt(int index)
    {
        m_innerList.RemoveAt(index);
    }

    public void CopyTo(T[] array)
    {
        m_innerList.CopyTo(array);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        m_innerList.CopyTo(array, arrayIndex);
    }

    public void Clear()
    {
        m_innerList.Clear();
    }

    public T this[int index]
    {
        get
        {
            return m_innerList[index];
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return m_innerList.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return m_innerList.GetEnumerator();
    }

    public int Count
    {
        get
        {
            return m_innerList.Count;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return false;
        }
    }

    public static int FindIndexForSortedInsert(List<T> list, Comparer<T> comparer, T item)
    {
        if (list.Count == 0)
        {
            return 0;
        }

        int lowerIndex = 0;
        int upperIndex = list.Count - 1;
        int comparisonResult;
        while (lowerIndex < upperIndex)
        {
            int middleIndex = (lowerIndex + upperIndex) / 2;
            T middle = list[middleIndex];
            comparisonResult = comparer.Compare(middle, item);
            if (comparisonResult == 0)
            {
                return middleIndex;
            }
            else if (comparisonResult > 0) // middle > item
            {
                upperIndex = middleIndex - 1;
            }
            else // middle < item
            {
                lowerIndex = middleIndex + 1;
            }
        }

        // At this point any entry following 'middle' is greater than 'item',
        // and any entry preceding 'middle' is lesser than 'item'.
        // So we either put 'item' before or after 'middle'.
        comparisonResult = comparer.Compare(list[lowerIndex], item);
        if (comparisonResult < 0) // middle < item
        {
            return lowerIndex + 1;
        }
        else
        {
            return lowerIndex;
        }
    }
}

Ответ 3

Я думаю, что причина в том, что у List<T> уже есть BinarySearch и Insert, что означает, что реализация вашего собственного всегда отсортированного списка тривиальна.

Не то, чтобы это означало, что класс SortedList<T> не входит в структуру - просто, что он, вероятно, не был очень высоким приоритетом, так как он легко мог быть быстро написан любым разработчиком, который в нем нуждался.

Я думаю, что то же самое верно для HashSet<T>, который изначально не существовал, потому что вы могли легко использовать Dictionary<T, byte> (например) для имитации одного перед .NET 3.5.

Я знаю, что то, что я делал в обоих случаях: у меня был класс UniqueSet<T> и класс AlwaysSortedList<T>, который просто обернул Dictionary<T, byte> и List<T> (и использовал BinarySearch и Insert), соответственно.

Ответ 4

Я думаю, что способ решить эту проблему - реализовать метод расширения, который добавляет к List<T> отсортированным образом (всего 2 строки кода;), а затем List<T> может использоваться как отсортированный список ( если вы не используете List.Add(...)):

    public static void AddSorted<T>(this List<T> list, T value)
    {
        int x = list.BinarySearch(value);
        list.Insert((x >= 0) ? x : ~x, value);
    }

Ответ 5

Это список с сортировкой, выполняемой ключом. Я просто размышляю, но, предоставляя возможность указывать ключ отдельно от элемента, ваш элемент не должен быть сопоставимым - только ключ должен быть. Я бы предположил, что в общем случае это экономит достаточное количество кода для разработки IComparable, поскольку ключ, скорее всего, является уже сопоставимым типом.

Ответ 6

Комментарии RPM вполне допустимы. Кроме того, с расширениями Linq вы можете сортировать по любому свойству T с помощью метода расширения Sort. Я думаю, что это может быть основной причиной этого.