如何使用单个值填充/实例化C#数组?

How to populate/instantiate a C# array with a single value?

我知道C中的值类型的实例化数组会自动填充该类型的默认值(例如,对于bool为false,对于int为0等)。

有没有一种方法可以用不是默认值的种子值自动填充数组?在创建之后还是内置方法(比如Java的数组?fILE())?假设我想要一个默认为真的布尔数组,而不是假数组。是否有一种内置的方法来实现这一点,或者您只需要使用for循环迭代整个数组?

1
2
3
4
5
6
7
8
9
10
 // Example pseudo-code:
 bool[] abValues = new[1000000];
 Array.Populate(abValues, true);

 // Currently how I'm handling this:
 bool[] abValues = new[1000000];
 for (int i = 0; i < 1000000; i++)
 {
     abValues[i] = true;
 }

必须遍历数组并将每个值"重置"为"真"似乎效率很低。这附近还有吗?也许通过翻转所有的值?

在输入这个问题并考虑之后,我猜想默认值只是C处理这些对象在后台的内存分配的结果,所以我想这可能是不可能的。但我还是想确定一下!


1
Enumerable.Repeat(true, 1000000).ToArray();


不知道框架方法,但您可以编写一个快速帮助器来为您完成它。

1
2
3
4
5
public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}


创建一个具有一千个true值的新数组:

1
var items = Enumerable.Repeat<bool>(true, 1000).ToArray();  // Or ToList(), etc.

同样,您可以生成整数序列:

2


对于大数组或可变大小的数组,您可能应该使用:

1
Enumerable.Repeat(true, 1000000).ToArray();

对于小数组,可以使用C 3中的集合初始化语法:

1
bool[] vals = new bool[]{ false, false, false, false, false, false, false };

集合初始化语法的好处是,您不必在每个槽中使用相同的值,并且可以使用表达式或函数来初始化槽。另外,我认为您可以避免将阵列插槽初始化为默认值的成本。例如:

1
bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };


如果数组太大,则应使用BitArray。它对每个bool使用1位,而不是字节(如bools数组中的字节),还可以使用位运算符将所有位设置为true。或者只是初始化为true。如果你只需要做一次,那只会花费更多。

1
2
3
4
5
System.Collections.BitArray falses = new System.Collections.BitArray(100000, false);
System.Collections.BitArray trues = new System.Collections.BitArray(100000, true);

// Now both contain only true values.
falses.And(trues);

好吧,经过一点谷歌搜索和阅读,我发现了:

1
2
bool[] bPrimes = new bool[1000000];
bPrimes = Array.ConvertAll<bool, bool>(bPrimes, b=> b=true);

这当然更接近我要找的。但我不确定这是否比在for循环中遍历原始数组并只更改值要好。事实上,经过一次快速测试后,它看起来慢了大约5倍。所以不是一个很好的解决方案!


不幸的是,我不认为有直接的方法,但是我认为您可以为数组类编写一个扩展方法来实现这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Program
{
    static void Main(string[] args)
    {
        int[] arr = new int[1000];
        arr.Init(10);
        Array.ForEach(arr, Console.WriteLine);
    }
}

public static class ArrayExtensions
{
    public static void Init<T>(this T[] array, T defaultVaue)
    {
        if (array == null)
            return;
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = defaultVaue;
        }
    }
}


或者…您可以简单地使用倒置逻辑。让false表示true,反之亦然。

代码样本

1
2
3
4
5
6
7
8
// bool[] isVisible = Enumerable.Repeat(true, 1000000).ToArray();
bool[] isHidden = new bool[1000000]; // Crazy-fast initialization!

// if (isVisible.All(v => v))
if (isHidden.All(v => !v))
{
    // Do stuff!
}


并行实现呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void InitializeArray<T>(T[] array, T value)
{
    var cores = Environment.ProcessorCount;

    ArraySegment<T>[] segments = new ArraySegment<T>[cores];

    var step = array.Length / cores;
    for (int i = 0; i < cores; i++)
    {
        segments[i] = new ArraySegment<T>(array, i * step, step);
    }
    var remaining = array.Length % cores;
    if (remaining != 0)
    {
        var lastIndex = segments.Length - 1;
        segments[lastIndex] = new ArraySegment<T>(array, lastIndex * step, array.Length - (lastIndex * step));
    }

    var initializers = new Task[cores];
    for (int i = 0; i < cores; i++)
    {
        var index = i;
        var t = new Task(() =>
        {
            var s = segments[index];
            for (int j = 0; j < s.Count; j++)
            {
                array[j + s.Offset] = value;
            }
        });
        initializers[i] = t;
        t.Start();
    }

    Task.WaitAll(initializers);
}

当只初始化一个数组时,这段代码的力量是看不到的,但我认为您绝对应该忘记的"纯"的。


下面的代码结合了小副本和数组的简单迭代。大副本的副本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    public static void Populate<T>( T[] array, int startIndex, int count, T value ) {
        if ( array == null ) {
            throw new ArgumentNullException("array" );
        }
        if ( (uint)startIndex >= array.Length ) {
            throw new ArgumentOutOfRangeException("startIndex","" );
        }
        if ( count < 0 || ( (uint)( startIndex + count ) > array.Length ) ) {
            throw new ArgumentOutOfRangeException("count","" );
        }
        const int Gap = 16;
        int i = startIndex;

        if ( count <= Gap * 2 ) {
            while ( count > 0 ) {
                array[ i ] = value;
                count--;
                i++;
            }
            return;
        }
        int aval = Gap;
        count -= Gap;

        do {
            array[ i ] = value;
            i++;
            --aval;
        } while ( aval > 0 );

        aval = Gap;
        while ( true ) {
            Array.Copy( array, startIndex, array, i, aval );
            i += aval;
            count -= aval;
            aval *= 2;
            if ( count <= aval ) {
                Array.Copy( array, startIndex, array, i, count );
                break;
            }
        }
    }

使用int[]数组的不同数组长度的基准是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
         2 Iterate:     1981 Populate:     2845
         4 Iterate:     2678 Populate:     3915
         8 Iterate:     4026 Populate:     6592
        16 Iterate:     6825 Populate:    10269
        32 Iterate:    16766 Populate:    18786
        64 Iterate:    27120 Populate:    35187
       128 Iterate:    49769 Populate:    53133
       256 Iterate:   100099 Populate:    71709
       512 Iterate:   184722 Populate:   107933
      1024 Iterate:   363727 Populate:   126389
      2048 Iterate:   710963 Populate:   220152
      4096 Iterate:  1419732 Populate:   291860
      8192 Iterate:  2854372 Populate:   685834
     16384 Iterate:  5703108 Populate:  1444185
     32768 Iterate: 11396999 Populate:  3210109

第一列是数组大小,然后是使用简单迭代(@jaredpared实现)进行复制的时间。这个方法的时间在那之后。这些是使用四个整数的结构数组的基准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
         2 Iterate:     2473 Populate:     4589
         4 Iterate:     3966 Populate:     6081
         8 Iterate:     7326 Populate:     9050
        16 Iterate:    14606 Populate:    16114
        32 Iterate:    29170 Populate:    31473
        64 Iterate:    57117 Populate:    52079
       128 Iterate:   112927 Populate:    75503
       256 Iterate:   226767 Populate:   133276
       512 Iterate:   447424 Populate:   165912
      1024 Iterate:   890158 Populate:   367087
      2048 Iterate:  1786918 Populate:   492909
      4096 Iterate:  3570919 Populate:  1623861
      8192 Iterate:  7136554 Populate:  2857678
     16384 Iterate: 14258354 Populate:  6437759
     32768 Iterate: 28351852 Populate: 12843259

这也有效……但可能是不必要的

1
2
 bool[] abValues = new bool[1000];
 abValues = abValues.Select( n => n = true ).ToArray<bool>();


如果您计划只设置数组中的一些值,但大多数时候想要获取(自定义)默认值,可以尝试如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SparseArray<T>
{
    private Dictionary<int, T> values = new Dictionary<int, T>();

    private T defaultValue;

    public SparseArray(T defaultValue)
    {
        this.defaultValue = defaultValue;
    }

    public T this [int index]
    {
      set { values[index] = value; }
      get { return values.ContainsKey(index) ? values[index] ? defaultValue; }
    }
}

您可能需要实现其他接口以使其有用,例如数组本身上的接口。


无法将数组中的所有元素设置为单个操作,除非该值是元素类型的默认值。

例如,如果它是一个整数数组,您可以通过一个操作将它们全部设置为零,如下所示:Array.Clear(...)


我知道我参加晚会迟到了,但有个主意。编写一个包装器,该包装器具有与包装值之间的转换运算符,以便它可以用作包装类型的代理。这实际上是受到了@l33t愚蠢的回答的启发。

首先(来自C++)我意识到在C语言中,当构建数组的元素时,不调用默认的CtoR。相反——即使存在用户定义的默认构造函数!--所有数组元素都初始化为零。那的确让我吃惊。

因此,一个包装类,它只提供一个默认的cTor与期望值将工作的数组在C++中,但不是在C.*。解决方法是让包装器类型在转换时将0映射到所需的种子值。这样一来,零初始化值就可以用seed进行初始化,从而达到所有实际目的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public struct MyBool
{
    private bool _invertedValue;

    public MyBool(bool b)
    {  
        _invertedValue = !b;
    }

    public static implicit operator MyBool(bool b)
    {
        return new MyBool(b);
    }

    public static implicit operator bool(MyBool mb)
    {
        return !mb._invertedValue;
    }

}

static void Main(string[] args)
{
        MyBool mb = false; // should expose false.
        Console.Out.WriteLine("false init gives false:"
                              + !mb);

        MyBool[] fakeBoolArray = new MyBool[100];

        Console.Out.WriteLine("Default array elems are true:"
                              + fakeBoolArray.All(b => b) );

        fakeBoolArray[21] = false;
        Console.Out.WriteLine("Assigning false worked:"
                              + !fakeBoolArray[21]);

        fakeBoolArray[21] = true;
        // Should define ToString() on a MyBool,
        // hence the !! to force bool
        Console.Out.WriteLine("Assigning true again worked:"
                              + !!fakeBoolArray[21]);
}

此模式适用于所有值类型。例如,如果需要使用4进行初始化等,则可以将int的0映射到4。

我很想在C++中做一个模板,提供种子值作为模板参数,但是我理解在C语言中是不可能的。还是我错过了什么?(当然,在C++映射中根本不需要,因为可以提供一个默认的CCTR,它将被调用数组元素。)

FWWW,这里有一个C++等价物:HTTPS://IDENNE.COM/WG8YYH。


如果可以反转逻辑,可以使用Array.Clear()方法将布尔数组设置为false。

1
2
3
4
5
        int upperLimit = 21;
        double optimizeMe = Math.Sqrt(upperLimit);

        bool[] seiveContainer = new bool[upperLimit];
        Array.Clear(seiveContainer, 0, upperLimit);

在创建数组的地方创建一个私有类,并为它创建一个getter和setter。除非您需要数组中的每个位置都是唯一的,比如随机的,否则使用int?作为数组,然后在get上,如果位置等于空,则填充该位置并返回新的随机值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IsVisibleHandler
{

  private bool[] b = new bool[10000];

  public bool GetIsVisible(int x)
  {
  return !b[x]
  }

  public void SetIsVisibleTrueAt(int x)
  {
  b[x] = false //!true
  }
}

或使用

1
2
3
4
public void SetIsVisibleAt(int x, bool isTrue)
{
b[x] = !isTrue;
}

二传手。


关于这个问题还有更多的答案(副本?)问题:在c中,memset的等价物是什么?

有人对替代方案进行了基准测试(包括不安全的版本,但他们没有尝试memset:http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.html


这里是另一个appraoch和System.Collections.BitArray,它有这样一个构造函数。

2

1
2
bool[] result = new bool[1000000];
new BitArray(1000000, true).CopyTo(result, 0);

1
2
3
Boolean[] data = new Boolean[25];

new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);