65.9K
CodeProject 正在变化。 阅读更多。
Home

循环列表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (12投票s)

2007 年 3 月 4 日

CPOL

2分钟阅读

viewsIcon

107405

downloadIcon

1032

循环列表实现

引言

我需要一个循环列表用于移动平均算法,并且看到 Code Project 上没有现成的,所以我想贡献一个。虽然有这篇文章,但我对它的实现方式有一些异议(也请阅读文章的评论)。

实现

我实现的循环列表使用一个单独的机制来管理循环索引,与枚举器使用的索引器分开。该列表还提供一个单独的机制来返回列表中项目的总数和填充项目的总数——换句话说,已经加载的项目。此外,枚举器仅迭代填充的项目,因此,如果您只有部分加载的列表,枚举器将返回填充的项目,而不是整个列表。

注意事项

  • 我在此类中使用了泛型,以便消除“装箱”问题,即把值类型转换为对象再转换回去。使用非泛型List类会将它的值存储为“object”类型,在转换为和从值类型和object类型时会降低性能。
  • 我特意没有List<T>派生CircularList,因为我不想继承 .NET 泛型 List<T>类的功能。
  • 循环列表通常是固定大小的,否则如何知道何时将索引循环到列表的开头?这被实现为一个简单的数组,也是不从 .NET 的 List<T>类派生的另一个原因。
  • CircularList实现了它自己的索引器,以便我们可以在索引器前进时获取和设置当前索引处的值。
  • 我添加了几个方法来清除列表并将其设置为特定值。
  • Count方法非常重要——它返回实际加载到循环列表中的项目数,而不是列表的总长度。这由Length属性处理。
  • CircularList支持一个单独的枚举索引器,并且从IEnumerator派生,以便该类可以在foreach语句中使用。
  • 您会注意到,即使这是一个循环列表,枚举器不会无限迭代!这一点非常重要。集合的“循环性”是在单独的方法中实现的,这样枚举器就不会无限循环遍历列表。

代码

using System;
using System.Collections.Generic;

namespace Clifton.Collections.Generic
{
  public class CircularList<T> : IEnumerable<T>, IEnumerator<T>
  {
    protected T[] items;
    protected int idx;
    protected bool loaded;
    protected enumIdx;

    /// <summary>
    /// Constructor that initializes the list with the 
    /// required number of items.
    /// </summary>
    public CircularList(int numItems)
    {
      if (numItems <= 0)
      {
        throw new ArgumentOutOfRangeException(
            "numItems can't be negative or 0.");
      }

      items = new T[numItems];
      idx = 0;
      loaded = false;
      enumIdx = -1;
    }

    /// <summary>
    /// Gets/sets the item value at the current index.
    /// </summary>
    public T Value
    {
      get { return items[idx]; }
      set { items[idx] = value; }
    }

    /// <summary>
    /// Returns the count of the number of loaded items, up to
    /// and including the total number of items in the collection.
    /// </summary>
    public int Count
    {
      get { return loaded ? items.Length : idx; }
    }

    /// <summary>
    /// Returns the length of the items array.
    /// </summary>
    public int Length
    {
      get { return items.Length; }
    }

    /// <summary>
    /// Gets/sets the value at the specified index.
    /// </summary>
    public T this[int index]
    {
      get 
      {
        RangeCheck(index);
        return items[index]; 
      }
      set 
      {
        RangeCheck(index);
        items[index] = value;
      }
    }

    /// <summary>
    /// Advances to the next item or wraps to the first item.
    /// </summary>
    public void Next()
    {
      if (++idx == items.Length)
      {
        idx = 0;
        loaded = true;
      }
    }

    /// <summary>
    /// Clears the list, resetting the current index to the 
    /// beginning of the list and flagging the collection as unloaded.
    /// </summary>
    public void Clear()
    {
      idx = 0;
      items.Initialize();
      loaded = false;
    }

    /// <summary>
    /// Sets all items in the list to the specified value, resets
    /// the current index to the beginning of the list and flags the
    /// collection as loaded.
    /// </summary>
    public void SetAll(T val)
    {
      idx = 0;
      loaded = true;

      for (int i = 0; i < items.Length; i++)
      {
        items[i] = val;
      }
    }

    /// <summary>
    /// Internal indexer range check helper. Throws
    /// ArgumentOutOfRange exception if the index is not valid.
    /// </summary>
    protected void RangeCheck(int index)
    {
      if (index < 0)
      {
        throw new ArgumentOutOfRangeException(
           "Indexer cannot be less than 0.");
      }

      if (index >= items.Length)
      {
        throw new ArgumentOutOfRangeException(
           "Indexer cannot be greater than or equal to the number of 
            items in the collection.");
      }
    }

    // Interface implementations:

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

    IEnumerator IEnumerable.GetEnumerator()
    {
      return this;
    }

    public T Current
    {
      get { return this[enumIdx]; }
    }

    public void Dispose()
    {
    }

    object IEnumerator.Current
    {
      get { return this[enumIdx]; }
    }

    public bool MoveNext()
    {
      ++enumIdx;
      bool ret = enumIdx < Count;

      if (!ret)
      {
        Reset();
      }

      return ret;
    }

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

用法

这是一个使用循环列表的示例。它填充了 10 个项目中的 5 个,将第一个项目设置为不同的值,然后遍历集合。结果显示是五个项目:“11, 1, 2, 3, 4”。

static void Test()
{
  CircularList<int> cl = new CircularList<int>(10);

  for (int i = 0; i < 5; i++)
  {
    cl.Value = i;
    cl.Next();
  }

  cl[0] = 11;

  foreach (int n in cl)
  {
    Console.WriteLine(n);
  }
}

结论

我希望人们在需要循环列表时会觉得这个类有用,例如,在处理移动平均时。

© . All rights reserved.