解决评论:
没有循环是否可以使用列表,因为我的性能因循环而下降
虽然不可能完全避免循环并保持相同的结构(但请参见下文),但我们可以大幅改进多个插入。
每次插入时,列表后面的所有元素都必须复制到内部结构中。因此,对于一个 n 元素列表,将有 n-1 复制操作,总共复制了 n * (n - 1) / 2 个元素(因为复制的元素数量每次都会下降。这意味着所花费的时间大约为 O(n 2)—二次时间。这是不好的。
我们可以做得更好,首先使列表大小增加一倍,然后将元素复制到它们的新位置。由于我们只需要复制 n * 2 副本,因此我们现在在 O(n)(线性时间)内完成此操作。
要将列表的大小加倍,我们将其添加到自身。这既方便(我们不需要创建另一个集合,或重复调用Add()),又利用了List<T>.AddRange() 优化处理添加到自身的列表这一事实:
list.AddRange(list);
现在,假设列表包含{"1", "2", "3"}。它现在包含{"1", "2", "3", "1", "2", "3"},我们想要{"1", "1", "2", "2", "3", "3"}。
所以我们从原始集合的最后一个元素 (list[2]) 开始并将其复制到最后两个位置,然后在列表中向后移动。因为我们在向前复制的同时向后移动,所以我们最终不会复制到尚未复制的位置(如果我们从头开始,我们只会在所有地方复制相同的"1"。
public static void DoupleUp(List<string> list)
{
if(list == null || list.Count == 0)
return;
list.AddRange(list);
for(int idx = list.Count / 2 - 1; idx != -1; --idx)
{
T el = list[idx];
list[idx * 2] = el;
list[idx * 2 + 1] = el;
}
}
现在剩下的就是意识到没有什么特别与字符串相关的,然后泛化该方法,以便我们可以在所有列表中使用它:
public static void DoupleUp<T>(List<T> list)
{
if(list == null || list.Count == 0)
return;
list.AddRange(list);
for(int idx = list.Count / 2 - 1; idx != -1; --idx)
{
T el = list[idx];
list[idx * 2] = el;
list[idx * 2 + 1] = el;
}
}
在修复了 Ankush 回答中的错误后,我进行了一次测试运行,从 Enumerable.Range(0, 10).Select(i => i.ToString()).ToList()(数字 0 到 9 作为字符串)开始,然后在上面调用了他的 Operation 15 次,然后又回到原来的状态list 并调用了我的DoubleUp 15 次(15 并不多,但每次都会翻倍,所以最后的调用是将 163840 个元素的列表变成一个包含 327680 个元素的列表)。
在我的机器上,使用 Operation 执行此操作大约需要 12.8 秒,而使用 DoubleUp 执行此操作大约需要 0.01 秒。
但是,如果您只想遍历此列表一次,那么您最好动态创建列表:
public static IEnumerable<T> DoubleElements<T>(this IEnumerable<T> source)
{
foreach(T item in source)
{
yield return item;
yield return item;
}
}
那么您可以只使用foreach(string str in DoubleElements(myarray)),甚至不必更改使用数组。
你甚至可以概括一下:
public static IEnumerable<T> RepeatElements<T>(this IEnumerable<T> source, int count)
{
foreach(T item in source)
for(int i = 0; i < count; ++i)
yield return item;
}
然后使用foreach(string str in RepeatElements(myarray, 2))。
现在,如果你真的需要避免循环,你将不得不再次做一些非常不同的事情,它会带走一些可能的进一步使用:
public class RepeatList<T> : IList<T>
{
private readonly IList<T> _backing;
private readonly int _repeats;
public RepeatList(IList<T> backing, int repeats)
{
if(backing == null)
throw new ArgumentNullException("backing");
if(repeats < 1)
throw new ArgumentOutOfRangeException("repeats");
_backing = backing;
_repeats = repeats;
}
public RepeatList(int repeats)
: this(new List<T>(), repeats)
{
}
public T this[int index]
{
get { return _backing[index / _repeats]; }
set { _backing[index / _repeats] = value; }
}
public int Count
{
get { return _backing.Count * _repeats; }
}
public bool IsReadOnly
{
get { return _backing.IsReadOnly; }
}
public int IndexOf(T item)
{
int idx = _backing.IndexOf(item);
return idx >= 0 ? idx * _repeats : -1;
}
public void Insert(int index, T item)
{
_backing.Insert(index / _repeats, item);
}
public void RemoveAt(int index)
{
_backing.RemoveAt(index / _repeats);
}
public void Add(T item)
{
_backing.Add(item);
}
public void Clear()
{
_backing.Clear();
}
public bool Contains(T item)
{
return _backing.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
if(array == null)
throw new ArgumentNullException("array");
if(arrayIndex < 0)
throw new ArgumentOutOfRangeException("arrayIndex");
if(array.Length - arrayIndex < _backing.Count * _repeats)
throw new ArgumentException("array is too small to copy all elements starting from index " + arrayIndex);
foreach(T item in this)
array[arrayIndex++] = item;
}
public bool Remove(T item)
{
return _backing.Remove(item);
}
public IEnumerator<T> GetEnumerator()
{
foreach(T item in _backing)
for(int i = 0; i != _repeats; ++i)
yield return item;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
这将在恒定时间内创建这样一个列表:var rep = new RepeatList<string>(myArray, 2) 几乎立即返回。
它会像 myArray 一样发生变异(更改 myArray 的元素 2,然后更改 rep 的元素 4 和 5),如果与非只读后备列表一起使用,这将是双向的,包括对 @987654348 的调用@ 实际上添加了 2 个元素。您可以通过将其设为只读并让所有变异成员抛出 NotSupportedException 来最大限度地减少奇怪,但发生直写也很有用。