Вопрос по default-value, arrays, c# – Как заполнить / создать экземпляр массива C # с одним значением?

164

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

Есть ли способ автоматического заполнения массива начальным значением, которое не является значением по умолчанию? Либо на создание, либо на последующий встроенный метод (например, Java).Arrays.fill ())? Скажем, я хотел логический массив, который по умолчанию был true, а не false. Есть ли встроенный способ сделать это, или вам просто нужно перебрать массив с циклом for?

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

Необходимость перебора массива и «перезагрузка» каждое значение к истине кажется неэффективным. Есть ли что-нибудь вокруг этого? Может быть, перевернув все значения?

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

Я обычно меняю имя с is_found на is_still_hiding. Любите ответы, хотя, мне нужно было сделать подобное для массива int в тестовом примере. (хороший вопрос) ctrl-alt-delor

Ваш Ответ

20   ответов
-2
Boolean[] data = new Boolean[25];

new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);
Пожалуйста, используйте лучшее форматирование и, возможно, несколько поясняющих слов, чтобы другие могли лучше понять ваше решение.
Вы можете использовать это для повышения производительности инициализации путем разбиения целевого массива и копирования начального числа в различные разделы. Это было только для того, чтобы дать представление - это мой первый и последний пост.
0

System.Collections.BitArray у которого есть такой конструктор.

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

или же

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

Array.Clear() метод для установки логического массива в false.

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

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

Или ... вы можете просто использовать инвертированную логику. Позволятьfalse имею в видуtrue и наоборот.

Code sample

// 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[] isVisible сделай этоbool[] isHidden
Забавное решение, хотя это будет намного сложнее, например, с помощью int, потому что вы теряете 0.
Я хотел бы увидеть код, который вы написали.
Люди реагируют так, будто это какой-то смешной взлом. Это обычная методика оптимизации. Если вам повезет, компилятор сделает это за вас.
57

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

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

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

var items = Enumerable.Range(0, 1000).ToArray();  // 0..999
Неплохо, но он все еще медленнее, чем цикл for, примерно в 4 раза patjbs
теоретически patjbs в будущем Enumerable.Repeat будет работать быстрее, поскольку будет использовать параллельную реализацию.
2

UNLESS, это значение является значением по умолчанию для типов элементов.

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

3

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

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

Возможно, вам потребуется реализовать другие интерфейсы, чтобы сделать его полезным, например,массив сам.

-1

Я проверил это для себя, и кажется, что метод Enumerable.Repeat работает в десять раз быстрее.

    [Test]
    public void Create_array_with_Array_Copy() {
        var size = 3000 * 30000;
        var arr = new int [size];
        var val = 0xFF;

        FillArray<int> (arr, val);
    }

    [Test]
    public void Create_array_with_loop () {
        var size    = 3000 * 30000;
        var arr     = new int [size];
        var val     = 0xFF;

        Populate<int> (arr, val);
    }

    [Test]
    public void Create_array_with_Repeat () {
        var size = 3000 * 30000;
        var arr = new int [size];
        var val = 0xFF;

        Enumerable.Repeat<int> (val, size);
    } 

    static void FillArray<T> (T [] arr, T fillValue) {
        int i = 0;
        if (arr.Length > 16) {
            {
                do {
                    arr [i++] = fillValue;
                } while (i < arr.Length);
                while (i + 16 < arr.Length) {
                    Array.Copy (arr, 0, arr, i, 16);
                    i = i + 16;
                }
            }
            while (i < arr.Length) {
                arr [i++] = fillValue;
            }
        }
    }

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

С уважением Ronny

Я проверил это для себя. Я получил время от проводника тестовых модулей. Но я видел, что третий тест (метод повторения) создает только перечислимый. Вы также должны добавить .ToArray () - и это делает его медленным.
Неясно, что должен делать код и, кроме того, как измеряется его производительность. Это не похоже на микробенчмарк.
6

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

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

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

173
Enumerable.Repeat(true, 1000000).ToArray();
В 20 раз медленнее? Как круто.
Чтобы увидеть реальный тест, посмотрите наC# Initialize Array.
Enumerable.ToArray не знает размер перечисляемой последовательности, поэтому он должен угадать размер массива. Это означает, что вы будете каждый раз получать массивыToArrayПревышен буфер, плюс еще одно выделение в конце для усечения. Также накладные расходы связаны с перечисляемым объектом.
Хотя это работает, это не очень хорошее решение, потому что оно очень медленное; фактически это примерно в 4 раза медленнее, чем итерация с циклом for. patjbs
да, это правда, когда мы рассматриваем производительность, цикл for быстрее
6

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

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

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;
        }
    }
}
Мне нравится идея расширения, чем больше я копаюсь в этом. Иногда простое и простое решение действительно лучшее! patjbs
1

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

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

Таким образом, класс-обертка, который просто предоставляет ctor по умолчанию с желаемым значением, будет работать для массивов в C ++, но не в C #. Обходной путь - позволить типу оболочки отобразить 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 для целых, если желательна инициализация с 4 и т. Д.

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

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

21

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

Есть еще несколько ответов на этот (дубликат?) Вопрос:Что эквивалентно memset в C #?

Кто-то оценил альтернативы (они включали небезопасную версию, но они не пыталисьmemset): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.html

22

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

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() };
И чтобы инициализировать массив float []:float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
Инициализация FWIW массива может быть выполнена в любой версии C #, например:bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
124

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

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}
i ++ копирует i, увеличивает i и возвращает исходное значение. ++ я просто возвращаю увеличенное значение. Поэтому ++ i быстрее, что может быть значительным в больших циклах, о которых мы говорим здесь.
@RobertDailey: Это оптимизация компилятора и больше не соответствует действительности. Я только что проверил свое убеждение: если возвращаемое значение i ++ ни для чего не используется, то компилятор автоматически скомпилирует его как ++ i. Кроме того, даже когда я использую возвращаемое значение, разница в производительности настолько мала, что мне нужно было привести крайний случай для его измерения. Даже тогда это приводило к разному времени выполнения всего на несколько процентов.
Я написал метод расширения, подобный этому, но он заставил его вернуть исходный массив, чтобы учесть цепочку методов, такую как:int[] arr = new int[16].Populate(-1);
Предпочитайте ++ i вместо i ++, если вам не нужна копия.
Хороший трюк. Это должно быть частью .Net по моему мнению.
3

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

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

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

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

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

Он создает новый массив (не меняет исходный экземпляр).
да, это похоже на цикл for просто делает работу примерно так же, как и все остальное patjbs
это похоже на то, что вы пытаетесь сделать, за исключением того, что оно вызывает функцию для каждого элемента в вашем массиве. Синтаксически это может выглядеть намного лучше, но это делает намного больше работы ...
0

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

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

Как сеттер.

Похожие вопросы