Создание кругового списка в C#?

каков был бы лучший способ создать круговой список в C#. Должен ли я получить его из коллекции LinkedList? Я планирую создать простую адресную книгу, используя этот связанный список для хранения моих контактов (это будет отстойная адресная книга, но мне все равно, потому что я буду единственным, кто ее использует). Я в основном просто хочу создать критически связанный список, чтобы я мог использовать его снова в других проектах.

Если вы не думаете, что это правильный путь дай мне знать, как будет лучше.

10 ответов


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

static class CircularLinkedList {
    public static LinkedListNode<T> NextOrFirst<T>(this LinkedListNode<T> current)
    {
        return current.Next ?? current.List.First;
    }

    public static LinkedListNode<T> PreviousOrLast<T>(this LinkedListNode<T> current)
    {
        return current.Previous ?? current.List.Last;
    }
}

теперь вы можете просто позвонить myNode.NextOrFirst () вместо myNode.Далее, и у вас будет все поведение кругового связанного списка. Вы все еще можете делать постоянные удаления времени и вставлять до и после всех узлов в списке и тому подобное. Если есть какой-то другой ключевой бит кругового связанного списка, я пропал, дай мне знать.


вероятно, было бы плохой идеей получить из класса BCL LinkedList. Этот класс разработан как некруглый список. Попытка сделать его круговым приведет только к проблемам.

вы, вероятно, гораздо лучше писать свой собственный.


Я не думаю, что круговой связанный список является правильной структурой данных для списка контактов. Простого List или Collection должно быть достаточно.


есть ли у вас конкретное требование использовать круговой список (т. е. домашнее задание)? Если нет, я бы предложил использовать простой List<T> класс для хранения контактов.


циклически связанные списки часто реализуются с использованием массивов, что делает их очень быстрыми и по своей природе не требует динамического изменения размера. Вам просто нужно быстро проверить индексы чтения и записи, чтобы увидеть, упали ли они с конца, и если да, сбросьте его до нуля (или одного, что угодно).

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


Я не думаю, что связанный список является наиболее эффективным способом для кругового буфера (исходный вопрос).

цель кругового буфера скорость и массив просто не может быть избит в контексте циклического буфера. Даже если вы сохраните указатель на последний элемент связанного списка, массив все равно будет более эффективный. Списки имеют возможности динамического изменения размера (накладные расходы), которые не нужны для круговых буферов. Сказав это, я думаю, что круговой буфер, вероятно, не является правильной структурой для приложения (списка контактов), о котором вы упоминаете.


class CircularArray<T> : IEnumerator<T>
{
    private readonly T[] array;
    private int index = -1;
    public T Current { get; private set; }

    public CircularArray(T[] array)
    {
        Current = default(T);
        this.array = array;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public bool MoveNext()
    {
        if (++index >= array.Length)
            index = 0;
        Current = array[index];
        return true;
    }

    public void Reset()
    {
        index = -1;
    }

    public void Dispose() { }
}

решение на основе модуля.

Если круговой буфер реализован как необработанный массив (или любой другой вид коллекции для чего это имеет значение)

T[] array;

и магазина в int current_index индекс текущего элемента, мы можем циклически вверх и вниз по буферу следующим образом:

T NextOrFirst()
{
    return array[(current_index + 1) % array.Length];
}

T PreviousOrLast()
{
    return array[(current_index + array.Length - 1) % array.Length];
}

тот же подход можно использовать с любой коллекцией Привязок XAML.



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


class Program
{
    static void Main(string[] args)
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7 };

        IEnumerable<int> circularNumbers = numbers.AsCircular();

        IEnumerable<int> firstFourNumbers = circularNumbers.Take(4); // 1 2 3 4
        IEnumerable<int> nextSevenNumbersfromfourth = circularNumbers
            .Skip(4).Take(7); // 4 5 6 7 1 2 3 
    }
}

public static class CircularEnumerable
{
    public static IEnumerable<T> AsCircular<T>(this IEnumerable<T> source)
    {
        if (source == null)
            yield break; // be a gentleman

        IEnumerator<T> enumerator = source.GetEnumerator();

        iterateAllAndBackToStart:
        while (enumerator.MoveNext()) 
            yield return enumerator.Current;

        enumerator.Reset();
        if(!enumerator.MoveNext())
            yield break;
        else
            yield return enumerator.Current;
goto iterateAllAndBackToStart;
    }
}

Если вы хотите пойти дальше, сделать CircularList и удерживайте тот же перечислитель, чтобы пропустить Skip() вращая как в вашем образце.