Как заполнить / создать экземпляр массива C # с одним значением? - PullRequest
174 голосов
/ 18 июня 2009

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

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

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

Ответы [ 20 ]

179 голосов
/ 18 июня 2009
Enumerable.Repeat(true, 1000000).ToArray();
129 голосов
/ 18 июня 2009

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

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}
61 голосов
/ 18 июня 2009

Создать новый массив с тысячей true значений:

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

Аналогично, вы можете генерировать целочисленные последовательности:

var items = Enumerable.Range(0, 1000).ToArray();  // 0..999
22 голосов
/ 18 июня 2009

Для больших массивов или массивов переменного размера вы, вероятно, должны использовать:

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() };
22 голосов
/ 24 июня 2010

Если ваш массив такой большой, вы должны использовать 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);
8 голосов
/ 18 июня 2009

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

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;
        }
    }
}
8 голосов
/ 18 июня 2009

Ну, после еще немного поисков и поиска я нашел это:

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

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

7 голосов
/ 06 февраля 2012

Или ... вы можете просто использовать инвертированную логику. Пусть 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!
}
6 голосов
/ 01 ноября 2013

Код ниже объединяет простую итерацию для маленьких копий и 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
6 голосов
/ 24 июня 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...