Как заполнить / создать экземпляр массива C# одним значением?

Я знаю, что инстанцированные массивы типов значений в C# автоматически заполняются значение по умолчанию типа (например, false для bool, 0 для int и т. д.).

есть ли способ автоматического заполнения массива начальным значением, которое не является значением по умолчанию? Либо при создании, либо после встроенного метода (например, Java массивы.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, кажется ineffecient. Есть ли что-нибудь вокруг этого? Может быть, перевернув все значения?

после ввода этого вопроса и размышления об этом я предполагаю, что значения по умолчанию являются просто результатом того, как C# обрабатывает выделение памяти этих объектов за кулисами, поэтому я предполагаю, что это, вероятно, невозможно сделать. Но я бы все равно хотелось бы знать наверняка!

10 ответов


Не знаю метода framework, но вы можете написать быстрый помощник, чтобы сделать это за вас.

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

создать новый массив с тысячей 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 };

преимущество синтаксиса инициализации коллекции заключается в том, что вам не нужно использовать одно и то же значение в каждом слоте, и вы можете использовать выражения или функции для инициализации слота. Кроме того, я думаю, что вы избегаете затрат на инициализацию слота массива до значения по умолчанию. Итак, для пример:

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

если Ваш массив настолько велик, вы должны использовать BitArray. Он использует 1 бит для каждого bool вместо байта (например, в массиве bools), также вы можете установить все биты в 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);

Ну после немного больше гуглить и читать я нашел это:

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

что, безусловно, ближе к тому, что я ищу. Но я не уверен, что это лучше, чем итерация через исходный массив в цикле for и просто изменение значений. После быстрого теста на самом деле он кажется медленнее примерно в 5 раз. Так что не очень хорошее решение!


к сожалению, я не думаю, что есть прямой путь, однако я думаю, что вы можете написать метод расширения для класса Array для этого

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

а как насчет параллельной реализации

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

при инициализации массива мощность этого кода не видна, но я думаю, что вы определенно должны забыть о "чистом" для.


код ниже сочетает в себе простую итерацию для небольших копий и массива.Копировать для больших копий

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