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

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

有没有一种方法来自动填充一个不是默认的种子值的数组? 无论是在创build或内置的方法(如Java的Arrays.fill() )? 假设我想要一个默认为true的布尔数组,而不是false。 有没有一个内置的方法来做到这一点,或者你只需​​要通过for循环遍历数组?

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

不得不遍历整个数组并将每个值“重置”为true似乎是无效的。 有没有办法解决? 也许通过翻转所有值?

在input这个问题并思考它之后,我猜测默认值只是C#如何在后台处理这些对象的内存分配的结果,所以我想这可能是不可能的。 但是我仍然很想知道!

不知道框架方法,但你可以写一个快速的帮手为你做。

 public static void Populate<T>(this T[] arr, T value ) { for ( int i = 0; i < arr.Length;i++ ) { arr[i] = value; } } 
 Enumerable.Repeat(true, 1000000).ToArray(); 

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

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

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

 var items = Enumerable.Range(0, 1000).ToArray(); // 0..999 

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

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

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

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

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

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

如果你的数组太大,你应该使用BitArray。 它为每个布尔使用1位而不是一个字节(就像在一个布尔数组中)也可以使用位操作符将所有位设置为true。 或者只是初始化为真。 如果你只需要做一次,它只会花费更多。

 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); 

不幸的是我不认为有一个直接的方式,但是我认为你可以写一个数组类的扩展方法来做到这一点

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

那么多一点googlesearch后,我发现这一点:

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

这当然更接近我所期待的。 但我不知道是否比在for循环中迭代原始数组更好,只是更改值。 事实上经过一个快速的testing,看起来比较慢了大约五分之一。所以这不是一个很好的解决scheme!

那么并行实现呢?

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

当只初始化一个数组时,这个代码的能力是看不到的,但是我认为你应该忘记“纯”的。

下面的代码将小副本的简单迭代和Array.Copy组合为大副本

  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 []数组的不同数组长度的基准是:

  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实现)进行复制的时间。 这种方法的时间是在那之后。 这些是使用四个整数结构数组的基准

  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 

或者…你可以简单地使用反向逻辑。 让false意思是true ,反之亦然。

代码示例

 // 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! } 

这也可以…但可能是不必要的

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

如果你打算只在数组中设置一些值,但是大部分时间想获取(自定义)默认值,你可以尝试这样的:

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

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

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

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

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

  int upperLimit = 21; double optimizeMe = Math.Sqrt(upperLimit); bool[] seiveContainer = new bool[upperLimit]; Array.Clear(seiveContainer, 0, upperLimit); 

我意识到我迟到了,但这里有个主意。 编写一个包装器,该包装器具有与包装值相匹配的转换运算符,以便它可以用作包装types的替代品。 这实际上是来自@ l33t的愚蠢回答。

首先(来自C ++)我意识到在C#中构造一个数组的元素时,不会调用默认的ctor。 相反 – 即使存在用户定义的默认构造函数! – 所有的数组元素都是零初始化的。 那让我感到惊讶。

所以一个简单的提供一个默认ctor的包装器类将会适用于C ++中的数组,而不是C#中的数组。 解决方法是让包装types在转换时将0映射到所需的种子值。 这样零初始值似乎是用种子初始化的所有实际目的:

 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]); } 

这种模式适用于所有的值types。 例如,如果需要初始化4,则可以将0到4映射为整数。

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

FWIW,这是一个C ++的等效: https : //ideone.com/wG8yEh 。

在这个(重复的?)问题上还有更多的答案: C#中的memset是什么?

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

这里是另一个System.Collections.BitArray具有这样的构造函数的appraoch。

 bool[] result = new BitArray(1000000, true).Cast<bool>().ToArray(); 

要么

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

在你创build数组的地方创build一个私有类,并为它创build一个getter和setter。 除非你需要数组中的每个位置是唯一的东西,如随机,然后使用int? 作为一个数组,然后取得,如果位置是相同的null填充该位置,并返回新的随机值。

 IsVisibleHandler { private bool[] b = new bool[10000]; public bool GetIsVisible(int x) { return !b[x] } public void SetIsVisibleTrueAt(int x) { b[x] = false //!true } } 

或者使用

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

作为二传手。

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