Сортировка списка по классу обертки - PullRequest
0 голосов
/ 15 марта 2012

У меня есть класс, который упаковывает список и добавляет функциональность для работы с коэффициентами. Это должно быть отсортировано двумя способами в разное время; по порядковому номеру и по величине отношения.

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

  1. Есть ли более совершенный способ сделать это?
  2. Как бы я это сделал, используя IComparer?

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

Приветствия
Berryl

/// <summary>
/// Maintains a list of ratios suitable for use in an allocation when the
/// sum of all items is exactly equal to one (100%). No individual it may
/// be less than zero or greater than one.
/// </summary>
public class RatioBag : IList<RatioAssociation>
{
    private readonly IList<RatioAssociation> _items = new List<RatioAssociation>();

    private Random _rnd;

    public void Order(IndexOrdering ordering) {
        IList<RatioAssociation> ordered = null;
        switch (ordering)
        {
            case IndexOrdering.FirstToLast:
                break;
            case IndexOrdering.LastToFirst:
                ordered = this.Reverse().ToList();
                break;
            case IndexOrdering.Random:
                if (_rnd == null) _rnd = new Random();
                ordered = Enumerable.Range(0, Count)
                    .OrderBy(x => _rnd.Next())
                    .Select(index => this[index])
                    .ToList();
                break;
            default:
                throw new ArgumentOutOfRangeException("ordering");
        }
        if (ordered == null) return;

        Clear();
        foreach (var ra in ordered) {
            Add(ra);
        }
    }

Ответы [ 3 ]

2 голосов
/ 15 марта 2012

Производительность вашего подхода не оптимальна, потому что вы не выполняете сортировку на месте.Вместо этого вы копируете каждый элемент дважды .Сначала при вызове ToList, а затем при вызове Add.

Интерфейс IComparer используется в сочетании с методом List.Sort, который делает на месте сортировки.Так что это должен быть путь.


Сначала вам нужно изменить тип _items на List<RatioAssociation>.

Для каждого заказа, который вы хотите поддержать, выпотребуется создать новый класс, который реализует IComparer.Если бы эти классы не использовались где-то еще, где вы могли бы поместить их как частные классы внутри RatioBag.

Ваш метод Order выглядел бы примерно так:Я бы предложил удалить перечисление IndexOrdering и указать один метод заказа для каждого типа заказа.Код будет чище.

1 голос
/ 15 марта 2012

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

Полагаю, это на самом деле не отвечает на ваш вопрос, но вместо передачи IComparer вы могли бы вместо этого передать Func <...> в ваш метод Order () (и исключить перечисление IndexOrdering), а затем передать это Func прямо в метод OrderBy ()? Это позволит вам выражать все виды сумасшедших методов сортировки, не касаясь этого класса, и вы сможете удалить из этого класса ненужный багаж состояния (например, _rnd, если он больше нигде не используется).

Что-то вроде:

public class RatioBag : IList<RatioAssociation>
{
    private readonly IList<RatioAssociation> _items = new List<RatioAssociation>();

    public void Order(Func<RatioAssociation, int> func) {
        var ordered = _items.OrderBy(func);
        _items.Clear();
        ((List<RatioAssociation>)_items).AddRange(ordered);
    }
}

тогда, чтобы назвать это:

var myCollection = new RatioBag();
myCollection.Order(item => item.PropertyX);
myCollection.Order(item => item.PropertyY);
var r = new Random();
myCollection.Order(item => r.Next());

Решение IComparer также позволило бы получить аналогичное решение.

Надеюсь, это поможет.

Nate

0 голосов
/ 15 марта 2012

Почему бы не использовать три разных метода вместо одного?

...