Утилита List <T>.Sort () против List <T>.OrderBy () для члена пользовательского класса контейнера - PullRequest
9 голосов
/ 16 июня 2010

Я обнаружил, что пробежался по старому устаревшему коду 3.5-фреймворка, и обнаружил некоторые моменты, когда есть целая куча списков и словарей, которые должны обновляться синхронизированным образом. Я решил, что могу сделать этот процесс бесконечно проще как для использования, так и для понимания, объединяя их в пользовательские классы контейнеров новых пользовательских классов. Однако есть некоторые моменты, в которых я пришел к вопросу об организации содержимого этих новых контейнерных классов по определенному внутреннему свойству. Например, сортировка по свойству ID номера одного класса.

Поскольку контейнерные классы в основном основаны на общем объекте List, моим первым инстинктом было написать внутренние классы с помощью IComparable и написать метод CompareTo, который сравнивает свойства. Таким образом, я могу просто позвонить items.Sort(), когда хочу вызвать сортировку.

Однако вместо этого я думал об использовании items = items.OrderBy(Func). Таким образом, это более гибко, если мне нужно отсортировать по любому другому свойству. Читаемость также лучше, так как свойство, используемое для сортировки, будет перечислено в строке с вызовом сортировки, вместо того, чтобы искать код IComparable. В результате общая реализация выглядит чище.

Меня не волнует преждевременная или микрооптимизация, но мне нравится последовательность. Я считаю, что лучше придерживаться одного вида реализации для всех случаев, когда это уместно, и использовать разные реализации там, где это необходимо. Стоит ли конвертировать мой код, чтобы использовать LINQ OrderBy вместо List.Sort? Лучше ли придерживаться реализации IComparable для этих пользовательских контейнеров? Есть ли какие-либо существенные механические преимущества, предлагаемые любым путем, на котором я должен взвешивать решение? Или их конечная функциональность эквивалентна тому, что она становится предпочтением кодера?

Ответы [ 2 ]

19 голосов
/ 16 июня 2010

Суть в том, что List<T>.Sort() выполняет сортировку на месте.Если ваш список открыт для внешнего кода, он всегда будет представлять один и тот же объект для этого кода.Это важно, если список хранится в поле с помощью кода вне класса контейнера.Если вы сортируете с OrderBy(), вы будете каждый раз получать новое перечисление, заменяя предыдущее items.Любой ранее сохраненный список не будет отображать текущее состояние вашего класса.

Учитывая производительность, OrderBy придется перебирать весь список для сортировки элементов.Затем вы вызовете ToList(), чтобы создать новый список из этого перечисления, повторяя этот список во второй раз.Кроме того, поскольку это перечисление, List будет использовать алгоритм удвоения, увеличивая его размер до тех пор, пока в него не поместится каждый элемент.В случае большого списка это может быть довольно много выделений и копирование памяти.Я ожидаю, что производительность будет намного хуже, чем List<T>.Sort().

Редактировать: Небольшой тест:

internal class Program {

    private static List<int> CreateList(int size) {

        // use the same seed so that every list has the same elements
        Random random = new Random(589134554);

        List<int> list = new List<int>(size);
        for (int i = 0; i < size; ++i)
            list.Add(random.Next());
        return list;
    }

    private static void Benchmark(int size, bool output = true) {
        List<int> list1 = CreateList(size);
        List<int> list2 = CreateList(size);

        Stopwatch stopwatch = Stopwatch.StartNew();
        list1.Sort();
        stopwatch.Stop();
        double elapsedSort = stopwatch.Elapsed.TotalMilliseconds;
        if (output)
            Console.WriteLine("List({0}).Sort(): {1}ms (100%)", size, elapsedSort);

        stopwatch.Restart();
        list2.OrderBy(i => i).ToList();
        stopwatch.Stop();
        double elapsedOrderBy = stopwatch.Elapsed.TotalMilliseconds;
        if (output)
            Console.WriteLine("List({0}).OrderBy(): {1}ms ({2:.00%})", size, elapsedOrderBy, elapsedOrderBy / elapsedSort);

    }

    internal static void Main() {

        // ensure linq library is loaded and initialized
        Benchmark(1000, false);

        Benchmark(10);
        Benchmark(100);
        Benchmark(1000);
        Benchmark(10000);
        Benchmark(100000);
        Benchmark(1000000);

        Console.ReadKey();
    }
}

Вывод (нормализовано в List.Sort):

List(10).Sort(): 0,0025ms (100%)
List(10).OrderBy(): 0,0157ms (628,00%)
List(100).Sort(): 0,0068ms (100%)
List(100).OrderBy(): 0,0294ms (432,35%)
List(1000).Sort(): 0,0758ms (100%)
List(1000).OrderBy(): 0,3107ms (409,89%)
List(10000).Sort(): 0,8969ms (100%)
List(10000).OrderBy(): 4,0751ms (454,35%)
List(100000).Sort(): 10,8541ms (100%)
List(100000).OrderBy(): 50,3497ms (463,88%)
List(1000000).Sort(): 124,1001ms (100%)
List(1000000).OrderBy(): 705,0707ms (568,15%)
4 голосов
/ 16 июня 2010

Как сказал Жульен, Sort (), вероятно, будет быстрее, однако, поскольку вы упомянули о лучшей читаемости и гибкости OrderBy (), вы можете добиться этого и с помощью метода Sort () (по крайней мере, если вы хотите использовать свойство Property). сравнимо по вашему виду)

items = items.OrderBy(x => x.Name).ToList();
items.Sort((x,y) => x.Name.CompareTo(y.Name)); // If x.Name is never null
items.Sort((x,y) => String.Compare(x.Name, y.Name)); // null safe
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...