pat*_*jbs 188 c# arrays default-value
我知道C#中实例化的值类型数组会自动填充类型的默认值(例如,对于bool为false,对于int为0等).
有没有办法使用不是默认值的种子值自动填充数组?之后是创建还是内置方法(比如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;
}
Run Code Online (Sandbox Code Playgroud)
必须遍历数组并将每个值"重置"为true似乎是无效的.有没有办法解决?也许通过翻转所有价值观?
在输出这个问题并思考之后,我猜测默认值只是C#如何在幕后处理这些对象的内存分配的结果,所以我想这可能不可能做到这一点.但我仍然想知道!
Ron*_*ony 182
Enumerable.Repeat(true, 1000000).ToArray();
Run Code Online (Sandbox Code Playgroud)
Jar*_*Par 136
不知道框架方法,但您可以编写一个快速帮助程序来为您完成.
public static void Populate<T>(this T[] arr, T value ) {
for ( int i = 0; i < arr.Length;i++ ) {
arr[i] = value;
}
}
Run Code Online (Sandbox Code Playgroud)
byt*_*der 68
创建一个包含一千个true值的新数组:
var items = Enumerable.Repeat<bool>(true, 1000).ToArray(); // Or ToList(), etc.
Run Code Online (Sandbox Code Playgroud)
同样,您可以生成整数序列:
var items = Enumerable.Range(0, 1000).ToArray(); // 0..999
Run Code Online (Sandbox Code Playgroud)
LBu*_*kin 23
对于大小可变的大型数组或数组,您应该使用:
Enumerable.Repeat(true, 1000000).ToArray();
Run Code Online (Sandbox Code Playgroud)
对于小数组,您可以使用C#3中的集合初始化语法:
bool[] vals = new bool[]{ false, false, false, false, false, false, false };
Run Code Online (Sandbox Code Playgroud)
集合初始化语法的好处是,您不必在每个插槽中使用相同的值,并且可以使用表达式或函数来初始化插槽.另外,我认为您可以避免将阵列插槽初始化为默认值的成本.所以,例如:
bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };
Run Code Online (Sandbox Code Playgroud)
MrF*_*Fox 23
如果你的数组太大,你应该使用BitArray.它对每个bool使用1位而不是一个字节(就像在bool数组中一样)也可以使用位运算符将所有位设置为true.或者只是初始化为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);
Run Code Online (Sandbox Code Playgroud)
Shu*_*rma 14
.NET Core 2.0及更高版本支持Array.Fill()方法。
这是示例代码。
var arr = new int[10];
int defaultValue = 2;
Array.Fill(arr,defaultValue);
Run Code Online (Sandbox Code Playgroud)
它还具有用于填充索引范围的重载方法。更多详情可在这找到。
不幸的是我不认为有直接的方法,但我认为你可以为数组类编写一个扩展方法来做到这一点
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
经过一番谷歌搜索和阅读后我发现了这个:
bool[] bPrimes = new bool[1000000];
bPrimes = Array.ConvertAll<bool, bool>(bPrimes, b=> b=true);
Run Code Online (Sandbox Code Playgroud)
这肯定更接近我正在寻找的东西.但是我不确定这是否比在for循环中迭代原始数组并且仅更改值更好.事实上经过快速测试后,它看起来慢了大约5倍.那么这不是一个好的解决方案!
或者......你可以简单地使用倒置逻辑.让我们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!
}
Run Code Online (Sandbox Code Playgroud)
您可以Array.Fill在.NET Core 2.0+和.NET Standard 2.1+中使用。
如果您使用的是 .NET Core、.NET Standard >= 2.1,或者依赖 System.Memory 包,您也可以使用该Span<T>.Fill()方法:
var valueToFill = 165;
var data = new int[100];
data.AsSpan().Fill(valueToFill);
// print array content
for (int i = 0; i < data.Length; i++)
{
Console.WriteLine(data[i]);
}
Run Code Online (Sandbox Code Playgroud)
https://dotnetfiddle.net/UsJ9bu
只是一个基准:
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.997 (1909/November2018Update/19H2)
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.302
[Host] : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT
.NET Core 3.1 : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT
Job=.NET Core 3.1 Runtime=.NET Core 3.1
| Method | Mean | Error | StdDev |
|----------------- |---------:|----------:|----------:|
| EnumerableRepeat | 2.311 us | 0.0228 us | 0.0213 us |
| NewArrayForEach | 2.007 us | 0.0392 us | 0.0348 us |
| ArrayFill | 2.426 us | 0.0103 us | 0.0092 us |
Run Code Online (Sandbox Code Playgroud)
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.997 (1909/November2018Update/19H2)
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.302
[Host] : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT
.NET Core 3.1 : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT
Job=.NET Core 3.1 Runtime=.NET Core 3.1
| Method | Mean | Error | StdDev |
|----------------- |---------:|----------:|----------:|
| EnumerableRepeat | 2.311 us | 0.0228 us | 0.0213 us |
| NewArrayForEach | 2.007 us | 0.0392 us | 0.0348 us |
| ArrayFill | 2.426 us | 0.0103 us | 0.0092 us |
Run Code Online (Sandbox Code Playgroud)
并行实现怎么样?
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);
}
Run Code Online (Sandbox Code Playgroud)
当只初始化一个数组时,无法看到这段代码的力量,但我认为你绝对应该忘记"纯粹".
下面的代码结合了小型副本的简单迭代和大型副本的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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用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
Run Code Online (Sandbox Code Playgroud)
第一列是数组大小,后面是使用简单迭代进行复制的时间(@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
Run Code Online (Sandbox Code Playgroud)
此处提供的许多答案都归结为一次将数组初始化一个元素的循环,它没有利用旨在一次对内存块进行操作的 CPU 指令。
.Net Standard 2.1(在撰写本文时为预览版)提供Array.Fill(),这有助于在运行时库中实现高性能(尽管截至目前,.NET Core似乎没有利用这种可能性) .
对于早期平台上的那些,当数组大小很重要时,以下扩展方法的性能明显优于普通循环。当我的在线代码挑战解决方案超出分配的时间预算约 20% 时,我创建了它。它减少了大约 70% 的运行时间。在这种情况下,数组填充是在另一个循环内执行的。BLOCK_SIZE 是由直觉而不是实验设置的。一些优化是可能的(例如复制所有已设置为所需值的字节而不是固定大小的块)。
internal const int BLOCK_SIZE = 256;
public static void Fill<T>(this T[] array, T value)
{
if (array.Length < 2 * BLOCK_SIZE)
{
for (int i = 0; i < array.Length; i++) array[i] = value;
}
else
{
int fullBlocks = array.Length / BLOCK_SIZE;
// Initialize first block
for (int j = 0; j < BLOCK_SIZE; j++) array[j] = value;
// Copy successive full blocks
for (int blk = 1; blk < fullBlocks; blk++)
{
Array.Copy(array, 0, array, blk * BLOCK_SIZE, BLOCK_SIZE);
}
for (int rem = fullBlocks * BLOCK_SIZE; rem < array.Length; rem++)
{
array[rem] = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)