Я знаю, что созданные экземпляры массивов типов значений в 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 # обрабатывает выделение памяти этими объектами за кулисами, поэтому я думаю, что, вероятно, это невозможно сделать. Но я все еще хотел бы знать наверняка!
источник
Ответы:
Не знаю метода фреймворка, но вы можете написать быстрый помощник, который сделает это за вас.
источник
int[] arr = new int[16].Populate(-1);
void
наT[]
и затем вы можете сделатьvar a = new int[100].Polupate(1)
источник
Enumerable.ToArray
не знает размер перечисляемой последовательности, поэтому он должен угадать размер массива. Это означает, что вы будете получать распределения массива каждый раз, когдаToArray
буфер будет превышен, плюс еще одно выделение в конце для усечения. Там также накладные расходы, связанные с перечислимым объектом.Создайте новый массив с тысячей
true
значений:Точно так же вы можете генерировать целочисленные последовательности:
источник
Для больших массивов или массивов переменного размера вы, вероятно, должны использовать:
Для небольшого массива вы можете использовать синтаксис инициализации коллекции в C # 3:
Преимущество синтаксиса инициализации коллекции состоит в том, что вам не нужно использовать одно и то же значение в каждом слоте, и вы можете использовать выражения или функции для инициализации слота. Кроме того, я думаю, что вы избегаете затрат на инициализацию слота массива значением по умолчанию. Так, например:
источник
float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Если ваш массив такой большой, вы должны использовать BitArray. Он использует 1 бит для каждого bool вместо байта (как в массиве bools), также вы можете установить все биты в true с помощью битовых операторов. Или просто инициализировать на true. Если вам нужно сделать это только один раз, это будет стоить дороже.
источник
Вы можете использовать
Array.Fill
в .NET Core 2.0+ и .NET Standard 2.1+.источник
к сожалению, я не думаю, что есть прямой путь, однако я думаю, что вы можете написать метод расширения для класса массива, чтобы сделать это
источник
Ну, после еще немного поиска в Google и чтения я нашел это:
Что, безусловно, ближе к тому, что я ищу. Но я не уверен, что это лучше, чем перебирать исходный массив в цикле for и просто изменять значения. После быстрого теста он кажется медленнее примерно в 5 раз. Так что тогда это не очень хорошее решение!
источник
Как насчет параллельной реализации
Когда только инициализируется массив, мощь этого кода не видна, но я думаю, что вы обязательно должны забыть о «чистом» для.
источник
Приведенный ниже код объединяет простую итерацию для маленьких копий и Array.Copy для больших копий.
Тесты для различной длины массива с использованием массива int []:
Первые столбцы - это размер массива, за которым следует время копирования с использованием простой итерации (реализация @JaredPared). Время этого метода после этого. Это тесты, использующие массив из структуры из четырех целых чисел
источник
Или ... вы можете просто использовать инвертированную логику. Пусть
false
значитtrue
и наоборот.Пример кода
источник
bool[] isVisible
чтобы делать этоbool[] isHidden
это тоже работает ... но может быть ненужным
источник
Многие из представленных здесь ответов сводятся к циклу, который инициализирует массив по одному элементу за раз, в котором не используются инструкции процессора, предназначенные для одновременной работы с блоком памяти.
.Net Standard 2.1 (в предварительном просмотре на момент написания этой статьи) предоставляет Array.Fill () , который обеспечивает высокопроизводительную реализацию в библиотеке времени выполнения (хотя на данный момент .NET Core , похоже, не использует эту возможность) ,
Для тех на более ранних платформах следующий метод расширения значительно превосходит тривиальный цикл, когда размер массива значительный. Я создал его, когда мое решение для онлайн-кода кода было около 20% от выделенного времени. Это сократило время выполнения примерно на 70%. В этом случае заполнение массива было выполнено внутри другого цикла. BLOCK_SIZE был задан чувством кишки, а не экспериментом. Возможна некоторая оптимизация (например, копирование всех байтов, которые уже установлены на желаемое значение, а не на блок фиксированного размера).
источник
Если вы планируете установить только несколько значений в массиве, но хотите большую часть времени получать (пользовательское) значение по умолчанию, вы можете попробовать что-то вроде этого:
Вам, вероятно, потребуется реализовать другие интерфейсы, чтобы сделать его полезным, например, для самого массива .
источник
Невозможно установить все элементы в массиве как одну операцию, UNLESS, это значение является значением по умолчанию для типов элементов.
Например, если это массив целых чисел, вы можете установить их все в ноль с помощью одной операции, например, так:
Array.Clear(...)
источник
Я понимаю, что опоздал на вечеринку, но вот идея. Напишите оболочку, у которой есть операторы преобразования в и из упакованного значения, чтобы его можно было использовать в качестве замены для упакованного типа. На самом деле это было вдохновлено глупо звучащим ответом @ l33t.
Во-первых (из C ++) я понял, что в C # ctor по умолчанию не вызывается при создании элементов массива. Вместо этого - даже при наличии определяемого пользователем конструктора по умолчанию! - все элементы массива инициализируются нулями. Это меня удивило.
Таким образом, класс-обертка, который просто предоставляет ctor по умолчанию с желаемым значением, будет работать для массивов в C ++, но не в C #. Обходной путь - позволить типу оболочки отобразить 0 на желаемое начальное значение после преобразования. Таким образом, нулевые инициализированные значения кажутся инициализированными с начальным числом для всех практических целей:
Этот шаблон применим ко всем типам значений. Можно, например, отобразить от 0 до 4 для целых, если желательна инициализация с 4 и т. Д.
Я хотел бы сделать его шаблон, как это было бы возможно в C ++, предоставив начальное значение в качестве параметра шаблона, но я понимаю, что это невозможно в C #. Или я что-то упустил? (Конечно, в C ++ отображение вообще не нужно, потому что можно предоставить ctor по умолчанию, который будет вызываться для элементов массива.)
FWIW, вот эквивалент C ++: https://ideone.com/wG8yEh .
источник
Если вы можете инвертировать свою логику, вы можете использовать
Array.Clear()
метод для установки логического массива в false.источник
Если вы используете .NET Core, .NET Standard> = 2.1 или зависите от пакета System.Memory, вы также можете использовать
Span<T>.Fill()
метод:https://dotnetfiddle.net/UsJ9bu
источник
Вот еще одна версия для нас, пользователей Framework, от которой отказались Microsoft. Это в 4 раза быстрее
Array.Clear
и быстрее, чем решение Panos Theof и параллельное решение Eric J и Petar Petrov. - в два раза быстрее для больших массивов.Сначала я хочу представить вам предка функции, потому что это облегчает понимание кода. С точки зрения производительности это в значительной степени наравне с кодом Panos Theof, а для некоторых вещей этого может уже хватить:
Как видите, это основано на повторном удвоении уже инициализированной части. Это просто и эффективно, но оно противоречит современной архитектуре памяти. Отсюда родилась версия, которая использует удвоение только для создания удобного для кэша начального блока, который затем итеративно обрабатывается по целевой области:
Примечание: предыдущий код был необходим в
(count + 1) >> 1
качестве ограничения для цикла удвоения, чтобы гарантировать, что в последней операции копирования будет достаточно фуража, чтобы покрыть все, что осталось. Это было бы не так для нечетных подсчетов, еслиcount >> 1
бы вместо этого использовались. Для текущей версии это не имеет значения, поскольку линейный цикл копирования устраняет любую слабину.Размер ячейки массива должен быть передан в качестве параметра, потому что - уму непостижимо - непатентованные средства не могут использоваться,
sizeof
если они не используют ограничение (unmanaged
), которое может или не может стать доступным в будущем. Неправильные оценки не имеют большого значения, но производительность является наилучшей, если значение является точным по следующим причинам:Недооценка размера элемента может привести к размерам блоков, превышающим половину кэша L1, что увеличивает вероятность того, что исходные данные копии будут удалены из L1, и их придется повторно выбирать из более медленных уровней кэша.
Завышение размера элемента приводит к недостаточному использованию кэша L1 ЦП, что означает, что цикл копирования линейного блока выполняется чаще, чем при оптимальном использовании. Таким образом, возникает больше фиксированных накладных расходов цикла / вызова, чем это строго необходимо.
Вот тест, с которым сравнивается мой код,
Array.Clear
и три других решения, упомянутых ранее. Время для заполнения целочисленных массивов (Int32[]
) заданных размеров. Чтобы уменьшить отклонения, вызванные капризами кеша и т. Д., Каждый тест был выполнен дважды, и время было взято для второго выполнения.Если производительность этого кода будет недостаточной, то многообещающим способом будет параллельное выполнение цикла линейного копирования (со всеми потоками, использующими один и тот же исходный блок) или нашего старого доброго друга P / Invoke.
Примечание: очистка и заполнение блоков обычно выполняются подпрограммами времени выполнения, которые переходят к узкоспециализированному коду с использованием инструкций MMX / SSE и тому подобного, поэтому в любой достойной среде можно просто вызвать соответствующий моральный эквивалент
std::memset
и быть уверенным в профессиональных уровнях производительности. Таким образом, по праву библиотечная функцияArray.Clear
должна оставлять все наши свернутые вручную версии в пыли. Тот факт, что все наоборот, показывает, насколько далеки от этого дела. То же самое относится и к тому, что нужно катиться самостоятельноFill<>
, потому что это все еще только в Core и Standard, но не в Framework. .NET существует уже почти двадцать лет, и нам все еще приходится P / Invoke влево и вправо для самых элементарных вещей или прокручивать свои собственные ...источник
Есть еще несколько ответов на этот (дубликат?) Вопрос: что эквивалентно memset в C #?
Кто-то сравнил альтернативы (они включали небезопасную версию, но не пытались
memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.htmlисточник
Вот еще один пример, с
System.Collections.BitArray
которым есть такой конструктор.или
источник
Создайте закрытый класс внутри, где вы создаете массив, и для него есть метод получения и установки. Если вам не нужно, чтобы каждая позиция в массиве была чем-то уникальным, например, случайным, тогда используйте int? как массив, а затем при получении, если позиция равна нулю, заполните эту позицию и верните новое случайное значение.
Или использовать
Как сеттер.
источник
источник