在数组中间添加元素

时间:2014-12-19 12:16:19

标签: c# arrays winforms

如何在数组中间添加元素?我试过在谷歌搜索,但没有循环就找不到办法。任何人都可以通过提供代码片段来帮助我,但请不要建议使用循环,因为阵列很重,性能是这里的瓶颈。

修改

实际上我希望每个奇数索引元素都被复制到偶数索引。

实施例

myarray[0] = "a";
myarray[1] = "b";
myarray[2] = "c";

预期答案

myarray[0] = "a";
myarray[1] = "a";
myarray[2] = "b";
myarray[3] = "b";
myarray[4] = "c";
myarray[5] = "c";

4 个答案:

答案 0 :(得分:7)

  

如何在数组中间添加元素?

您无法在数组中的任何位置添加元素。创建数组后,其大小是固定的 - 您可以修改元素,但不能添加或删除它们。

您可以改为使用List<T> 允许插入 - 但是需要复制插入点之后出现的元素。

您可能会使用LinkedList<T>代替,但是您需要循环才能获取到正确的节点。

您可能需要考虑使用某种树结构,这样可以更容易找到正确的位置,然后插入数据。

或者,考虑重新设计 - 我们没有太多的上下文,但您可能想要考虑收集所有数据开始,忽略排序,然后将其排序为正确的顺序。

编辑:对于您的示例,我不会使用任何类型的插入 - 我会从旧的列表中创建一个新列表,例如:

// Or use Enumerable.Repeat instead of creating an array for each element.
var newList = oldList.SelectMany(x => new[] { x, x }).ToList();

或者没有LINQ:

var copies = 2; // Or whatever
var newList = new List<string>(oldList.Count * copies);
foreach (var item in oldList)
{
    for (int i = 0; i < copies; i++)
    {
        newList.Add(item);
    }
}

从根本上说,你不会比O(N)更便宜,其中N是结果列表中的项目数...

答案 1 :(得分:1)

你可以使用&#34; Buffer.BlockCopy&#34;但你必须为它创建一个单独的数组副本

答案 2 :(得分:1)

您无法在Array中添加元素。但是,如果您使用List,则方法如下:

public void Operation(List<string> list)
{
    if (list == null || list.Count == 0) return;

    int count = list.Count;

    for (int i = 0; i <= count; i++)
        if (i % 2 == 0)
            list.Insert(i + 1, list[i]);
} 

答案 3 :(得分:1)

发表评论:

  

没有循环可以使用list,因为我的性能因为循环而下降

虽然不可能完全避免循环并保持相同的结构(但见下文),但我们可以大量改进多个插入。

每次执行插入操作时,列表中稍后的所有元素都必须在内部结构中复制。因此,对于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 \ n}到处都是。

"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;
  }
}

在修复Ankush的答案后,我做了一个测试运行,我开始使用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; } } (数字0到9作为字符串),然后在其上调用他的Enumerable.Range(0, 10).Select(i => i.ToString()).ToList() 15时间,然后回到原始列表并调用我的Operation 15次(15不多,但每次都加倍,所以最后的调用是将163840个元素的列表变成一个具有327680个元素的元素)

在我的机器上使用DoubleUp执行此操作需要大约12.8秒,而使用Operation执行此操作大约需要0.01秒。

如果你只想重复一次这个列表,那么你可以更好地创建列表:

DoubleUp

然后你可以使用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的元素2并更改myArray的元素4和5),如果与非只读备份列表一起使用,则双向,包括实际添加2个元素的rep调用。您可以通过使其成为只读并使所有变异成员抛出Add来最小化这种奇怪性,但是对于直写也是有用的。