wake-up-neo.com

Wie kann man ein C # -Array mit einem einzelnen Wert füllen/instanziieren?

Ich weiß, dass instanziierte Arrays von Werttypen in C # automatisch mit dem Standardwert des Typs gefüllt werden (z. B. false für bool, 0 für int usw.).

Gibt es eine Möglichkeit, ein Array mit einem Ausgangswert zu füllen, der nicht der Standardwert ist? Entweder beim Erstellen oder einer integrierten Methode danach (wie Javas Arrays.fill () )? Angenommen, ich wollte ein boolesches Array, das standardmäßig true war, anstatt false. Gibt es eine integrierte Möglichkeit, dies zu tun, oder müssen Sie das Array nur mit einer for-Schleife durchlaufen?

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

Das Array durchlaufen und jeden Wert auf true "zurücksetzen" scheint uneffektiv zu sein. Gibt es überhaupt etwas um dieses herum? Vielleicht durch Umdrehen aller Werte?

Nach dem Eingeben dieser Frage und dem Nachdenken, denke ich, sind die Standardwerte einfach ein Ergebnis davon, wie C # die Speicherzuordnung dieser Objekte hinter den Kulissen handhabt. Ich denke, es ist wahrscheinlich nicht möglich. Aber ich würde es trotzdem gerne wissen!

162
patjbs

Sie kennen keine Framework-Methode, aber Sie könnten einen schnellen Helfer schreiben, der dies für Sie erledigt.

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}
122
JaredPar
Enumerable.Repeat(true, 1000000).ToArray();
171
Rony

Erstellen Sie ein neues Array mit tausend true-Werten:

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

Ebenso können Sie Ganzzahlsequenzen erzeugen:

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

Für große Arrays oder Arrays mit variabler Größe sollten Sie wahrscheinlich Folgendes verwenden:

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

Für kleine Arrays können Sie die Collection-Initialisierungssyntax in C # 3 verwenden:

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

Der Vorteil der Collection-Initialisierungssyntax besteht darin, dass Sie nicht in jedem Steckplatz denselben Wert verwenden müssen und dass Sie einen Steckplatz mit Ausdrücken oder Funktionen initialisieren können. Ich denke auch, dass Sie die Kosten für die Initialisierung des Array-Slots auf den Standardwert vermeiden. Also zum Beispiel:

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

Wenn Ihr Array so groß ist, sollten Sie BitArray verwenden. Es verwendet 1 Bit für jeden Bool anstelle eines Bytes (wie in einem Array von Bools). Sie können auch alle Bits mit Bitoperatoren auf "True" setzen. Oder einfach auf wahr initialisieren. Wenn Sie es nur einmal tun müssen, kostet es nur mehr.

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);
21
MrFox

Nach ein bisschen mehr googeln und lesen fand ich folgendes:

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

Welches ist sicherlich näher an dem, was ich suche. Ich bin mir jedoch nicht sicher, ob das besser ist, als das ursprüngliche Array in einer for-Schleife zu durchlaufen und nur die Werte zu ändern. Nach einem Schnelltest scheint es um etwa den Faktor 5 langsamer zu sein. Also nicht wirklich eine gute Lösung!

8
patjbs

leider glaube ich nicht, dass es einen direkten Weg gibt, aber ich denke, dass Sie eine Erweiterungsmethode für die Array-Klasse schreiben können, um dies zu tun

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
bashmohandes

Was ist mit einer parallelen Implementierung?

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

Wenn nur ein Array initialisiert wird, kann die Leistungsfähigkeit dieses Codes nicht gesehen werden, aber ich denke, Sie sollten das "pure" unbedingt vergessen.

6
Petar Petrov

Oder ... Sie könnten einfach eine invertierte Logik verwenden. Sei falsetrue und umgekehrt.

Codebeispiel

// 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
l33t

Der folgende Code kombiniert eine einfache Iteration für kleine Kopien und Array.Copy für große Kopien

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

Die Benchmarks für unterschiedliche Arraylängen bei Verwendung eines int [] - Arrays sind:

         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

Die erste Spalte enthält die Arraygröße, gefolgt vom Zeitpunkt des Kopierens mit einer einfachen Iteration (@JaredPared-Implementierung). Die Zeit dieser Methode ist danach ... Diese sind die Benchmarks, die ein Array mit einer Struktur von vier Ganzzahlen verwenden

         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
Panos Theof

das funktioniert auch ... aber möglicherweise unnötig

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

Sie können Array.Fill in .NET Core 2.0+ und .NET Standard 2.1+ verwenden.

3
juFo

Wenn Sie planen, nur einige Werte im Array festzulegen, die meiste Zeit jedoch den (benutzerdefinierten) Standardwert abrufen möchten, können Sie Folgendes versuchen:

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

Sie werden wahrscheinlich andere Schnittstellen implementieren müssen, um sie nützlich zu machen, z. B. diejenigen auf array selbst.

3
Douglas

Wenn Sie Ihre Logik invertieren können, können Sie die Methode Array.Clear() verwenden, um das boolesche Array auf false zu setzen.

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

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

Es ist nicht möglich, alle Elemente in einem Array als eine einzige Operation festzulegen, UNLESS. Dieser Wert ist der Standardwert der Elementtypen. 

Wenn es sich beispielsweise um ein Array von Ganzzahlen handelt, können Sie sie mit einem einzigen Vorgang auf Null setzen, z. B.: Array.Clear(...)

2
James

Viele der hier vorgestellten Antworten laufen auf eine Schleife hinaus, die das Array Element für Element initialisiert, ohne die CPU-Befehle zu nutzen, die für die gleichzeitige Verarbeitung eines Speicherblocks ausgelegt sind.

.Net Standard 2.1 (in der Vorschau zum jetzigen Zeitpunkt) bietet Array.Fill () , was sich für eine leistungsstarke Implementierung in der Laufzeitbibliothek anbietet (obwohl .NET Core ab sofort - scheint nicht zu sein nutze diese Möglichkeit).

Bei früheren Plattformen übertrifft die folgende Erweiterungsmethode eine einfache Schleife um einen erheblichen Teil, wenn die Arraygröße erheblich ist. Ich habe es erstellt, als meine Lösung für eine Online-Code-Herausforderung etwa 20% über dem zugewiesenen Zeitbudget lag. Es reduzierte die Laufzeit um rund 70%. In diesem Fall wurde das Füllen des Arrays in einer anderen Schleife durchgeführt. BLOCK_SIZE wurde eher durch Bauchgefühl als durch Experimente festgelegt. Einige Optimierungen sind möglich (z. B. Kopieren aller bereits auf den gewünschten Wert gesetzten Bytes anstelle eines Blocks mit fester Größe).

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;
        }
    }
}
1
Eric J.

Ich weiß, dass ich zu spät zur Party komme, aber hier ist eine Idee. Schreiben Sie einen Wrapper mit Konvertierungsoperatoren in und aus dem umschlossenen Wert, damit er als Stand-In für den umschriebenen Typ verwendet werden kann. Dies wurde tatsächlich durch die dumm klingende Antwort von @ l33t inspiriert.

Zuerst (aus C++ stammend) wurde mir klar, dass in C # kein Standard-Ctor aufgerufen wird, wenn die Elemente eines Arrays erstellt werden. Stattdessen - auch in Gegenwart eines benutzerdefinierten Standardkonstruktors! - Alle Array-Elemente werden mit Null initialisiert. Das hat mich überrascht.

Eine Wrapper-Klasse, die einfach einen Standard-Ctor mit dem gewünschten Wert bereitstellt, funktioniert also für Arrays in C++, jedoch nicht für C #. Eine Problemumgehung besteht darin, den Wrapper-Typ bei der Konvertierung dem gewünschten Startwert 0 zuordnen zu lassen. Auf diese Weise scheinen null initialisierte Werte für alle praktischen Zwecke mit dem Startwert initialisiert zu werden:

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

Dieses Muster gilt für alle Werttypen. Man könnte zum Beispiel 0 bis 4 für ints zuordnen, wenn eine Initialisierung mit 4 gewünscht wird usw.

Ich würde gerne eine Vorlage davon erstellen, wie es in C++ möglich wäre, und den Startwert als Vorlagenparameter angeben, aber ich weiß, dass dies in C # nicht möglich ist. Oder fehlt mir etwas? (Natürlich ist in C++ Mapping überhaupt nicht notwendig, da man einen Standard-Ctor angeben kann, der für Array-Elemente aufgerufen wird.)

FWIW, hier ist ein C++ - Äquivalent: https://ideone.com/wG8yEh .

1

Erstellen Sie eine private Klasse, in der Sie das Array erstellen und einen Getter und Setter dafür haben. Wenn Sie nicht benötigen, dass jede Position im Array einzigartig ist, wie zufällig, dann verwenden Sie int? als Array und dann auf get, wenn die Position gleich null ist, füllen Sie diese Position und geben Sie den neuen Zufallswert zurück.

IsVisibleHandler
{

  private bool[] b = new bool[10000];

  public bool GetIsVisible(int x)
  {
  return !b[x]
  }

  public void SetIsVisibleTrueAt(int x)
  {
  b[x] = false //!true
  }
}

Oder verwenden 

public void SetIsVisibleAt(int x, bool isTrue)
{
b[x] = !isTrue;
}

Als Setzer.

0
Peter J

Hier ist ein weiterer Ansatz mit System.Collections.BitArray, der einen solchen Konstruktor hat.

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

oder 

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

Es gibt einige weitere Antworten auf diese (doppelte?) Frage: Was ist das Äquivalent von memset in C #?

Jemand hat die Alternativen geprüft (sie enthielten eine unsichere Version, versuchten jedoch nicht memset): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.html

0
Rich