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

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

Есть ли способ автоматического заполнения массива с начальным значением, которое не является значением по умолчанию? Или после создания или встроенного метода (например, Java Arrays.fill())? Скажем, мне нужен логический массив, который по умолчанию был истинным, а не 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;
 }

Чтобы выполнить итерацию по массиву и "reset", каждое значение в true кажется неэффективным. Во всяком случае, вокруг? Может быть, перевернув все значения?

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

Если ваш массив настолько велик, вы должны использовать 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);

Ответ 5

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

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

Для малого массива вы можете использовать синтаксис инициализации коллекции в С# 3:

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

В приведенном ниже коде используется простая итерация для небольших копий и массив 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

Ответ 11

это также работает... но может быть ненужным

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

Ответ 12

Если вы планируете устанавливать только несколько значений в массиве, но хотите получить значение по умолчанию (custom) по умолчанию, вы можете попробовать что-то вроде этого:

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.

Ответ 13

Вы можете использовать Array.Fill в .NET Core 2. 0+ и .NET Standard 2. 1+.

Ответ 14

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

Например, если это массив целых чисел, вы можете установить их все в ноль с помощью одной операции, например: Array.Clear(...)

Ответ 15

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

Сначала (из С++) я понял, что в С# по умолчанию ctor не вызывается, когда строятся элементы массива. Вместо этого - даже при наличии пользовательского конструктора по умолчанию! - все элементы массива инициализируются нулем. Это меня удивило.

Таким образом, класс-оболочка, который просто предоставляет значение по умолчанию ctor с требуемым значением, будет работать для массивов в С++, но не в С#. Обходной путь заключается в том, чтобы позволить оболочке типа 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]);
}

Этот шаблон применим ко всем типам значений. Например, можно было бы использовать карту от 0 до 4 для int, если была желательна инициализация с 4 и т.д.

Я хотел бы создать шаблон, как это было бы возможно на С++, предоставив начальное значение в качестве параметра шаблона, но я понимаю, что это невозможно в С#. Или я чего-то не хватает? (Конечно, в С++-сопоставлении вообще не требуется, потому что можно предоставить по умолчанию ctor, который будет вызываться для элементов массива.)

FWIW, здесь эквивалент С++: https://ideone.com/wG8yEh.

Ответ 16

Если вы можете инвертировать свою логику, вы можете использовать метод Array.Clear(), чтобы установить логический массив в значение false.

        int upperLimit = 21;
        double optimizeMe = Math.Sqrt(upperLimit);

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

Ответ 17

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

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

Ответ 19

Вот еще одна оценка с System.Collections.BitArray, которая имеет такой конструктор.

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

или

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

Ответ 20

Сделайте частный класс внутри, где вы создаете массив, и у него есть геттер и сеттер. Если вам не нужно, чтобы каждая позиция в массиве была чем-то уникальным, например, случайным, а затем использовать int? как массив, а затем на get, если позиция равна нулю, заполните эту позицию и верните новое случайное значение.

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

Как установщик.

Ответ 21

Boolean[] data = new Boolean[25];

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