단일 값으로 C # 배열을 채우거나 인스턴스화하는 방법은 무엇입니까?
C #에서 인스턴스화 된 값 유형의 배열은 유형의 기본값 으로 자동으로 채워진다는 것을 알고 있습니다 (예 : bool의 경우 false, int의 경우 0 등).
기본값이 아닌 시드 값으로 배열을 자동으로 채우는 방법이 있습니까? 나중에 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;
}
배열을 반복하고 각 값을 true로 "재설정"하는 것은 비효율적입니다. 어쨌든 주위에 있습니까? 어쩌면 모든 가치를 뒤집어서?
이 질문을 입력하고 그것에 대해 생각한 후에, 기본값은 C #이 장면 뒤에서 이러한 객체의 메모리 할당을 처리하는 방법의 결과 일 뿐이라고 생각합니다. 그러나 나는 아직도 확실하게 알고 싶다!
프레임 워크 방법을 모르지만 빠른 도우미를 작성하면됩니다.
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
배열이 너무 큰 경우 BitArray를 사용해야합니다. 부울 배열과 같이 바이트 대신 모든 부울에 1 비트를 사용하며 비트 연산자로 모든 비트를 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);
가변 크기의 대형 배열 또는 배열의 경우 다음을 사용해야합니다.
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() };
불행히도 직접적인 방법은 없다고 생각하지만 배열 클래스의 확장 메소드를 작성하여이를 수행 할 수 있다고 생각합니다.
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;
}
}
}
조금 더 인터넷 검색하고 읽은 후에 나는 이것을 발견했다.
bool[] bPrimes = new bool[1000000];
bPrimes = Array.ConvertAll<bool, bool>(bPrimes, b=> b=true);
내가 찾고있는 것에 확실히 더 가깝습니다. 그러나 for 루프에서 원래 배열을 반복하고 값을 변경하는 것보다 낫다는 것이 확실하지 않습니다. 실제로 빠른 테스트를 한 후에는 약 5 배 정도 느려 보입니다. 따라서 좋은 해결책은 아닙니다!
아니면 ... 간단히 역 논리를 사용할 수도 있습니다. 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!
}
병렬 구현은 어떻습니까?
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);
}
배열을 초기화 할 때이 코드의 힘을 볼 수는 없지만 "순수"를 잊어 버려야한다고 생각합니다.
아래 코드는 작은 복사본에 대한 간단한 반복과 큰 복사본에 대한 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 구현)을 사용한 복사 시간입니다. 이 방법의 시간은 그 후입니다. 다음은 4 개의 정수로 구성된 구조체 배열을 사용하는 벤치 마크입니다.
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
이것은 또한 작동하지만 불필요 할 수 있습니다
bool[] abValues = new bool[1000];
abValues = abValues.Select( n => n = true ).ToArray<bool>();
배열에 몇 가지 값만 설정하려고하지만 대부분의 경우 (사용자 정의) 기본값을 얻으려면 다음과 같이 시도하십시오.
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.Fill
.NET Core 2.0 이상 및 .NET Standard 2.1 이상에서 사용할 수 있습니다 .
배열의 모든 요소를 단일 작업 (UNLESS)으로 설정하는 방법은 없습니다. 해당 값은 요소 유형 기본값입니다.
예를 들어, 정수 배열이면 단일 조작으로 모두 0으로 설정할 수 있습니다. Array.Clear(...)
나는 파티에 늦었다는 것을 알고 있지만 여기에 아이디어가 있습니다. 랩핑 된 유형의 독립형으로 사용될 수 있도록 랩핑 된 값과의 변환 연산자가있는 랩퍼를 작성하십시오. 이것은 실제로 @ l33t의 바보 같은 대답에서 영감을 얻었습니다.
첫 번째 (C ++에서 제공) C #에서는 배열 요소가 생성 될 때 기본 ctor가 호출되지 않는다는 것을 깨달았습니다. 대신 사용자 정의 기본 생성자가있는 경우에도! -모든 배열 요소는 0으로 초기화됩니다. 그것은 나를 놀라게했다.
따라서 원하는 값으로 기본 ctor를 제공하는 래퍼 클래스는 C ++에서는 아니지만 C ++에서는 배열에서 작동합니다. 해결 방법은 변환시 랩퍼 유형이 원하는 시드 값에 0을 맵핑하도록하는 것입니다. 그렇게하면 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]);
}
This pattern is applicable to all value types. One could for example map 0 to 4 for ints if initialization with 4 was desired etc.
I'd love to make a template of it as would be possible in C++, providing the seed value as template parameter, but I understand that's not possible in C#. Or am I missing something? (Of course in C++ mapping is not necessary at all because one can provide a default ctor which will be called for array elements.)
FWIW, here's a C++ equivalent: https://ideone.com/wG8yEh .
If you can invert your logic you can use the Array.Clear()
method to set the boolean array to false.
int upperLimit = 21;
double optimizeMe = Math.Sqrt(upperLimit);
bool[] seiveContainer = new bool[upperLimit];
Array.Clear(seiveContainer, 0, upperLimit);
Many of the answers presented here boil down to a loop that initializes the array one element at a time, which does not take advantage of CPU instructions designed to operate on a block of memory at once.
.Net Standard 2.1 (in preview as of this writing) provides Array.Fill(), which lends itself to a high-performance implementation in the runtime library (though as of now, .NET Core doesn't seem to leverage that possibility).
For those on earlier platforms, the following extension method outperforms a trivial loop by a substantial margin when the array size is significant. I created it when my solution for an online code challenge was around 20% over the allocated time budget. It reduced the runtime by around 70%. In this case, the array fill was performed inside another loop. BLOCK_SIZE was set by gut feeling rather than experiment. Some optimizations are possible (e.g. copying all bytes already set to the desired value rather than a fixed-size block).
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;
}
}
}
There are some more answers on this (duplicate?) question: What is the equivalent of memset in C#?
Someone has benchmarked the alternatives (they included an unsafe version, but they didn't try memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.html
Here is another appraoch with System.Collections.BitArray
which has such a constructor.
bool[] result = new BitArray(1000000, true).Cast<bool>().ToArray();
or
bool[] result = new bool[1000000];
new BitArray(1000000, true).CopyTo(result, 0);
Make a private class inside where you make the array and the have a getter and setter for it. Unless you need each position in the array to be something unique, like random, then use int? as an array and then on get if the position is equal null fill that position and return the new random value.
IsVisibleHandler
{
private bool[] b = new bool[10000];
public bool GetIsVisible(int x)
{
return !b[x]
}
public void SetIsVisibleTrueAt(int x)
{
b[x] = false //!true
}
}
Or use
public void SetIsVisibleAt(int x, bool isTrue)
{
b[x] = !isTrue;
}
As setter.
Boolean[] data = new Boolean[25];
new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);
'Programming' 카테고리의 다른 글
시퀀스로 시작하지 않는 문자열에 대한 정규식 (0) | 2020.05.18 |
---|---|
Bash 함수에서 부울 리턴 (0) | 2020.05.17 |
Microsoft.Jet.OLEDB.4.0 '공급자가 로컬 컴퓨터에 등록되어 있지 않습니다 (0) | 2020.05.17 |
Python에서 Pearson 상관 관계 및 의미 계산 (0) | 2020.05.17 |
strftime을 사용하여 Python datetime을 epoch로 변환 (0) | 2020.05.17 |