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 automatisch mit einem Startwert zu füllen, der nicht der Standardwert ist? Entweder bei der Erstellung oder eine integrierte Methode danach (wie Java Arrays.fill () )? Angenommen, ich wollte ein boolesches Array, das standardmäßig true anstelle von false ist. 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;
}
Es scheint ineffizient zu sein, das Array zu durchlaufen und jeden Wert auf true zurückzusetzen. Gibt es das überhaupt? Vielleicht durch Umdrehen aller Werte?
Nachdem ich diese Frage abgetippt und darüber nachgedacht habe, schätze ich, dass die Standardwerte einfach darauf zurückzuführen sind, wie C # die Speicherzuweisung dieser Objekte hinter den Kulissen handhabt. Ich kann mir also vorstellen, dass dies wahrscheinlich nicht möglich ist. Aber ich würde es trotzdem gerne sicher wissen!
quelle
Antworten:
Sie kennen keine Framework-Methode, aber Sie könnten einen schnellen Helfer schreiben, um dies für Sie zu tun.
quelle
int[] arr = new int[16].Populate(-1);
void
zuT[]
und dann können Sie tunvar a = new int[100].Polupate(1)
quelle
Enumerable.ToArray
kennt die Größe der aufzählbaren Sequenz nicht, muss also die Arraygröße erraten. Das bedeutet, dass Sie jedes Mal Array-Zuweisungen erhalten, wennToArray
der Puffer überschritten wird, plus eine weitere Zuweisung am Ende für das Trimmen. Mit dem aufzählbaren Objekt ist auch ein Overhead verbunden.Erstellen Sie ein neues Array mit tausend
true
Werten:Ebenso können Sie ganzzahlige Sequenzen generieren:
quelle
Für große Arrays oder Arrays mit variabler Größe sollten Sie wahrscheinlich Folgendes verwenden:
Für kleine Arrays können Sie die Syntax für die Initialisierung der Sammlung in C # 3 verwenden:
Der Vorteil der Syntax für die Sammlungsinitialisierung besteht darin, dass Sie nicht in jedem Slot denselben Wert verwenden müssen und Ausdrücke oder Funktionen zum Initialisieren eines Slots verwenden können. Ich denke auch, dass Sie die Kosten für die Initialisierung des Array-Steckplatzes auf den Standardwert vermeiden. Also zum Beispiel:
quelle
float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Wenn Ihr Array so groß ist, sollten Sie BitArray verwenden. Es wird 1 Bit für jeden Bool anstelle eines Bytes verwendet (wie in einem Array von Bools). Außerdem können Sie mit Bitoperatoren alle Bits auf true setzen. Oder initialisieren Sie einfach auf true. Wenn Sie es nur einmal tun müssen, kostet es jedoch nur mehr.
quelle
Sie können
Array.Fill
in .NET Core 2.0+ und .NET Standard 2.1+ verwenden.quelle
Leider glaube ich nicht, dass es einen direkten Weg gibt, aber ich denke, Sie können eine Erweiterungsmethode für die Array-Klasse schreiben, um dies zu tun
quelle
Nun, nachdem ich ein bisschen mehr gegoogelt und gelesen hatte, fand ich Folgendes:
Welches ist sicherlich näher an dem, was ich suche. Ich bin mir jedoch nicht sicher, ob dies besser ist, als das ursprüngliche Array in einer for-Schleife zu durchlaufen und nur die Werte zu ändern. Nach einem kurzen Test erscheint es tatsächlich um den Faktor 5 langsamer. Dann also keine wirklich gute Lösung!
quelle
Was ist mit einer parallelen Implementierung?
Wenn Sie nur ein Array initialisieren, kann die Leistung dieses Codes nicht gesehen werden, aber ich denke, Sie sollten auf jeden Fall das "reine" für vergessen.
quelle
Der folgende Code kombiniert einfache Iteration für kleine Kopien und Array.Copy für große Kopien
Die Benchmarks für unterschiedliche Array-Längen mit einem int [] -Array sind:
Die erste Spalte gibt die Arraygröße an, gefolgt vom Zeitpunkt des Kopierens mithilfe einer einfachen Iteration (@ JaredPared-Implementierung). Die Zeit dieser Methode ist danach. Dies sind die Benchmarks, die ein Array aus einer Struktur von vier Ganzzahlen verwenden
quelle
Oder ... Sie könnten einfach invertierte Logik verwenden. Lassen Sie
false
meinentrue
und umgekehrt.Codebeispiel
quelle
bool[] isVisible
machenbool[] isHidden
das funktioniert auch ... könnte aber unnötig sein
quelle
Viele der hier vorgestellten Antworten beschränken sich auf eine Schleife, die das Array elementweise initialisiert und dabei nicht die CPU-Anweisungen nutzt, die für den gleichzeitigen Betrieb eines Speicherblocks ausgelegt sind.
.Net Standard 2.1 (in der Vorschau zum jetzigen Zeitpunkt ) bietet Array.Fill () , das sich für eine Hochleistungsimplementierung in der Laufzeitbibliothek eignet (obwohl .NET Core dies derzeit nicht zu tun scheint Möglichkeit nutzen ). .
Für diejenigen auf früheren Plattformen übertrifft die folgende Erweiterungsmethode eine triviale Schleife um einen erheblichen Vorsprung, wenn die Arraygröße signifikant 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 die Array-Füllung in einer anderen Schleife durchgeführt. BLOCK_SIZE wurde eher durch Bauchgefühl als durch Experiment eingestellt. Einige Optimierungen sind möglich (z. B. Kopieren aller bereits auf den gewünschten Wert eingestellten Bytes anstelle eines Blocks mit fester Größe).
quelle
Wenn Sie nur einige der Werte im Array festlegen möchten, aber die meiste Zeit den (benutzerdefinierten) Standardwert erhalten möchten, können Sie Folgendes versuchen:
Sie müssen wahrscheinlich andere Schnittstellen implementieren, um sie nützlich zu machen, z. B. die auf dem Array selbst.
quelle
Es gibt keine Möglichkeit, alle Elemente in einem Array als einzelne Operation festzulegen, AUSSER dieser Wert ist der Standardwert des Elementtyps.
Wenn es sich beispielsweise um ein Array von Ganzzahlen handelt, können Sie sie alle mit einer einzigen Operation auf Null setzen:
Array.Clear(...)
quelle
Mir ist klar, dass ich zu spät zur Party komme, aber hier ist eine Idee. Schreiben Sie einen Wrapper mit Konvertierungsoperatoren zum und vom umschlossenen Wert, damit er als Ersatz für den umschlossenen Typ verwendet werden kann. Dies wurde tatsächlich von der albern klingenden Antwort von @ l33t inspiriert.
Zuerst (aus C ++) wurde mir klar, dass in C # ein Standard-Ctor nicht aufgerufen wird, wenn die Elemente eines Arrays erstellt werden. Stattdessen - auch bei Vorhandensein eines benutzerdefinierten Standardkonstruktors! - Alle Array-Elemente sind nullinitialisiert. 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 in C #. Eine Problemumgehung besteht darin, den Wrapper-Typ bei der Konvertierung 0 dem gewünschten Startwert zuordnen zu lassen. Auf diese Weise scheinen null initialisierte Werte für alle praktischen Zwecke mit dem Startwert initialisiert zu werden:
Dieses Muster gilt für alle Werttypen. Man könnte zum Beispiel 0 bis 4 für Ints abbilden, wenn eine Initialisierung mit 4 gewünscht wäre usw.
Ich würde gerne eine Vorlage daraus erstellen, wie dies in C ++ möglich wäre, wobei der Startwert als Vorlagenparameter angegeben wird, aber ich verstehe, dass dies in C # nicht möglich ist. Oder fehlt mir etwas? (Natürlich ist in C ++ die Zuordnung überhaupt nicht erforderlich, da man einen Standard-Ctor bereitstellen kann, der für Array-Elemente aufgerufen wird.)
FWIW, hier ist ein C ++ - Äquivalent: https://ideone.com/wG8yEh .
quelle
Wenn Sie Ihre Logik invertieren können, können Sie
Array.Clear()
das boolesche Array mit der Methode auf false setzen.quelle
Wenn Sie .NET Core, .NET Standard> = 2.1 verwenden oder vom System.Memory-Paket abhängig sind, können Sie auch die folgende
Span<T>.Fill()
Methode verwenden:https://dotnetfiddle.net/UsJ9bu
quelle
Hier ist eine weitere Version für uns Framework-Benutzer, die von Microsoft aufgegeben wurde. Es ist viermal so schnell
Array.Clear
und schneller als die Lösung von Panos Theof und die parallele Lösung von Eric J und Petar Petrov - bis zu zweimal so schnell für große Arrays.Zuerst möchte ich Ihnen den Vorfahren der Funktion vorstellen, da dies das Verständnis des Codes erleichtert. In Bezug auf die Leistung entspricht dies ziemlich genau dem Code von Panos Theof, und für einige Dinge, die möglicherweise bereits ausreichen:
Wie Sie sehen können, basiert dies auf der wiederholten Verdoppelung des bereits initialisierten Teils. Dies ist einfach und effizient, verstößt jedoch gegen moderne Speicherarchitekturen. Daher wurde eine Version geboren, die das Verdoppeln nur verwendet, um einen cachefreundlichen Startblock zu erstellen, der dann iterativ über den Zielbereich gestrahlt wird:
Hinweis: Der frühere Code wird
(count + 1) >> 1
als Begrenzung für die Verdopplungsschleife benötigt, um sicherzustellen, dass der endgültige Kopiervorgang über genügend Futter verfügt, um alles verbleibende abzudecken. Dies wäre bei ungeraden Zählungen nicht der Fall, wenncount >> 1
stattdessen verwendet würde. Für die aktuelle Version ist dies nicht von Bedeutung, da die lineare Kopierschleife einen Durchhang aufnimmt.Die Größe einer Array-Zelle muss als Parameter übergeben werden, da Generika - es ist ein Rätsel - nicht verwendet werden dürfen, es
sizeof
sei denn, sie verwenden eine Einschränkung (unmanaged
), die möglicherweise in Zukunft verfügbar wird oder nicht. Falsche Schätzungen sind keine große Sache, aber die Leistung ist aus folgenden Gründen am besten, wenn der Wert korrekt ist:Eine Unterschätzung der Elementgröße kann zu Blockgrößen führen, die größer als die Hälfte des L1-Cache sind, wodurch die Wahrscheinlichkeit erhöht wird, dass Kopierquellendaten aus L1 entfernt werden und aus langsameren Cache-Ebenen erneut abgerufen werden müssen.
Das Überschätzen der Elementgröße führt zu einer Unterauslastung des L1-Cache der CPU, was bedeutet, dass die lineare Blockkopierschleife häufiger ausgeführt wird als bei optimaler Auslastung. Somit entsteht mehr Overhead für feste Schleifen / Anrufe als unbedingt erforderlich.
Hier ist ein Benchmark, an dem mein Code
Array.Clear
und die anderen drei zuvor genannten Lösungen verglichen werden. Die Timings dienen zum Füllen von Integer-Arrays (Int32[]
) der angegebenen Größen. Um die durch Cache-Abweichungen usw. verursachten Abweichungen zu verringern, wurde jeder Test zweimal hintereinander ausgeführt, und die Zeitpunkte wurden für die zweite Ausführung festgelegt.Sollte die Leistung dieses Codes nicht ausreichen, wäre ein vielversprechender Weg die Parallelisierung der linearen Kopierschleife (wobei alle Threads denselben Quellblock verwenden) oder unseres guten alten Freundes P / Invoke.
Hinweis: Das Löschen und Füllen von Blöcken erfolgt normalerweise über Laufzeitroutinen, die mithilfe von MMX / SSE-Anweisungen und so weiter zu hochspezialisiertem Code verzweigen. In jeder anständigen Umgebung würde man einfach das jeweilige moralische Äquivalent
std::memset
eines professionellen Leistungsniveaus nennen und sich dessen versichern. IOW, von Rechts wegen sollte die BibliotheksfunktionArray.Clear
alle unsere handgerollten Versionen im Staub liegen lassen. Die Tatsache, dass es umgekehrt ist, zeigt, wie weit die Dinge wirklich entfernt sind. Gleiches gilt fürFill<>
das erstmalige Eigenrollen, da es sich immer noch nur um Core und Standard handelt, nicht aber um das Framework. .NET gibt es schon seit fast zwanzig Jahren und wir müssen immer noch links und rechts P / Invoke für die grundlegendsten Dinge oder unsere eigenen ...quelle
Es gibt weitere Antworten auf diese (doppelte?) Frage: Was entspricht dem Memset in C #?
Jemand hat die Alternativen bewertet (sie enthielten eine unsichere Version, aber sie haben es nicht versucht
memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.htmlquelle
Hier ist ein weiterer Ansatz, mit
System.Collections.BitArray
dem ein solcher Konstruktor hat.oder
quelle
Erstellen Sie eine private Klasse, in der Sie das Array erstellen und einen Getter und Setter dafür haben. Wenn Sie nicht möchten, dass jede Position im Array etwas Einzigartiges ist, wie zufällig, 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.
Oder verwenden
Als Setter.
quelle
quelle