Je sais que les tableaux instanciés de types de valeurs en C # sont automatiquement remplis avec la valeur par défaut du type (par exemple, false pour bool, 0 pour int, etc.).
Existe-t-il un moyen de remplir automatiquement un tableau avec une valeur de départ qui n'est pas la valeur par défaut? Soit lors de la création ou d'une méthode intégrée par la suite (comme Arrays.fill () de Java )? Disons que je voulais un tableau booléen qui était vrai par défaut, au lieu de faux. Existe-t-il un moyen intégré de le faire, ou avez-vous juste à parcourir le tableau avec une boucle 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;
}
Devoir parcourir le tableau et "réinitialiser" chaque valeur à true semble inefficace. Y a-t-il de toute façon autour de ça? Peut-être en inversant toutes les valeurs?
Après avoir tapé cette question et y avoir réfléchi, je suppose que les valeurs par défaut sont simplement le résultat de la façon dont C # gère l'allocation de mémoire de ces objets en arrière-plan, donc j'imagine que ce n'est probablement pas possible de le faire. Mais j'aimerais quand même savoir avec certitude!
la source
Réponses:
Je ne connais pas de méthode de framework mais vous pouvez écrire une aide rapide pour le faire pour vous.
la source
int[] arr = new int[16].Populate(-1);
void
àT[]
et ensuite vous pouvez le fairevar a = new int[100].Polupate(1)
la source
Enumerable.ToArray
ne connaît pas la taille de la séquence énumérable, il doit donc deviner la taille du tableau. Cela signifie que vous obtiendrez des allocations de tableau chaque fois queToArray
le tampon est dépassé, plus une allocation de plus à la fin pour le découpage. Il y a aussi des frais généraux impliqués avec l'objet énumérable.Créez un nouveau tableau avec mille
true
valeurs:De même, vous pouvez générer des séquences entières:
la source
Pour les grands tableaux ou les tableaux de taille variable, vous devriez probablement utiliser:
Pour les petits tableaux, vous pouvez utiliser la syntaxe d'initialisation de la collection en C # 3:
L'avantage de la syntaxe d'initialisation de la collection est que vous n'avez pas à utiliser la même valeur dans chaque emplacement et que vous pouvez utiliser des expressions ou des fonctions pour initialiser un emplacement. De plus, je pense que vous évitez le coût de l'initialisation de l'emplacement de la baie à la valeur par défaut. Ainsi, par exemple:
la source
float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Si votre tableau est si grand, vous devez utiliser BitArray. Il utilise 1 bit pour chaque booléen au lieu d'un octet (comme dans un tableau de booléens), vous pouvez également définir tous les bits sur true avec les opérateurs de bits. Ou tout simplement initialiser sur vrai. Si vous ne devez le faire qu'une seule fois, cela ne coûtera que plus.
la source
Vous pouvez utiliser
Array.Fill
dans .NET Core 2.0+ et .NET Standard 2.1+.la source
malheureusement, je ne pense pas qu'il existe un moyen direct, mais je pense que vous pouvez écrire une méthode d'extension pour la classe de tableau pour ce faire
la source
Eh bien, après un peu plus de recherche sur Google et de lecture, j'ai trouvé ceci:
Ce qui est certainement plus proche de ce que je recherche. Mais je ne sais pas si c'est mieux que d'itérer à travers le tableau d'origine dans une boucle for et de simplement changer les valeurs. Après un test rapide en fait, il semble plus lent d'environ un facteur 5. Donc ce n'est pas vraiment une bonne solution!
la source
Qu'en est-il d'une implémentation parallèle
Lors de l'initialisation d'un tableau uniquement, la puissance de ce code ne peut pas être vue, mais je pense que vous devez absolument oublier le "pur" pour.
la source
Le code ci-dessous combine une itération simple pour les petites copies et Array.Copy pour les grandes copies
Les repères pour différentes longueurs de tableau à l'aide d'un tableau int [] sont:
Les premières colonnes sont la taille du tableau, suivie du temps de copie à l'aide d'une simple itération (implémentation @JaredPared). Le temps de cette méthode est après cela. Ce sont les repères utilisant un tableau d'une structure de quatre entiers
la source
Ou ... vous pouvez simplement utiliser une logique inversée. Soit
false
méchanttrue
et vice versa.Exemple de code
la source
bool[] isVisible
fairebool[] isHidden
cela fonctionne aussi ... mais pourrait être inutile
la source
La plupart des réponses présentées ici se résument à une boucle qui initialise le tableau un élément à la fois, qui ne tire pas parti des instructions du processeur conçues pour fonctionner sur un bloc de mémoire à la fois.
.Net Standard 2.1 (dans l'aperçu au moment de la rédaction de cet article) fournit Array.Fill () , qui se prête à une implémentation hautes performances dans la bibliothèque d'exécution (bien que pour l'instant, .NET Core ne semble pas tirer parti de cette possibilité) .
Pour ceux sur les plates-formes antérieures, la méthode d'extension suivante surpasse une boucle triviale d'une marge substantielle lorsque la taille du tableau est importante. Je l'ai créé lorsque ma solution pour un défi de code en ligne était d'environ 20% supérieure au budget alloué. Il a réduit le temps d'exécution d'environ 70%. Dans ce cas, le remplissage du tableau a été effectué dans une autre boucle. BLOCK_SIZE a été défini par l'intestin plutôt que par l'expérience. Certaines optimisations sont possibles (par exemple, copier tous les octets déjà définis à la valeur souhaitée plutôt qu'un bloc de taille fixe).
la source
Si vous prévoyez de ne définir que quelques-unes des valeurs dans le tableau, mais que vous souhaitez obtenir la valeur par défaut (personnalisée) la plupart du temps, vous pouvez essayer quelque chose comme ceci:
Vous devrez probablement implémenter d'autres interfaces pour le rendre utile, telles que celles du tableau lui-même.
la source
Il n'y a aucun moyen de définir tous les éléments d'un tableau comme une seule opération, À MOINS QUE cette valeur soit la valeur par défaut des types d'élément.
Par exemple, s'il s'agit d'un tableau d'entiers, vous pouvez tous les mettre à zéro avec une seule opération, comme ceci:
Array.Clear(...)
la source
Je me rends compte que je suis en retard à la fête mais voici une idée. Écrivez un encapsuleur qui a des opérateurs de conversion vers et depuis la valeur encapsulée afin qu'il puisse être utilisé comme remplaçant pour le type encapsulé. Cela a en fait été inspiré par la réponse stupide de @ l33t.
Tout d'abord (venant de C ++), j'ai réalisé qu'en C #, un ctor par défaut n'est pas appelé lorsque les éléments d'un tableau sont construits. Au lieu de cela - même en présence d'un constructeur par défaut défini par l'utilisateur! - tous les éléments du tableau sont initialisés à zéro. Cela m'a surpris.
Ainsi, une classe wrapper qui fournit simplement un ctor par défaut avec la valeur souhaitée fonctionnerait pour les tableaux en C ++ mais pas en C #. Une solution de contournement consiste à laisser le type de wrapper mapper 0 à la valeur de départ souhaitée lors de la conversion. De cette façon, les valeurs initialisées nulles semblent être initialisées avec la graine à toutes fins pratiques:
Ce modèle est applicable à tous les types de valeurs. On pourrait par exemple mapper 0 à 4 pour les entiers si l'initialisation avec 4 était souhaitée, etc.
J'adorerais en faire un modèle comme cela serait possible en C ++, en fournissant la valeur de départ comme paramètre de modèle, mais je comprends que ce n'est pas possible en C #. Ou est-ce que je manque quelque chose? (Bien sûr, en mappage C ++ n'est pas du tout nécessaire car on peut fournir un ctor par défaut qui sera appelé pour les éléments du tableau.)
FWIW, voici un équivalent C ++: https://ideone.com/wG8yEh .
la source
Si vous pouvez inverser votre logique, vous pouvez utiliser la
Array.Clear()
méthode pour définir le tableau booléen sur false.la source
Si vous êtes sur .NET Core, .NET Standard> = 2.1, ou dépendez du package System.Memory, vous pouvez également utiliser la
Span<T>.Fill()
méthode:https://dotnetfiddle.net/UsJ9bu
la source
Voici une autre version pour nous, les utilisateurs du Framework abandonnés par Microsoft. Il est 4 fois plus vite que
Array.Clear
et plus rapide que la solution de Panos Theof et Eric J de et un parallèle Petar Petrov - jusqu'à deux fois plus rapide pour les grands tableaux.Je veux d'abord vous présenter l'ancêtre de la fonction, car cela facilite la compréhension du code. En termes de performances, cela correspond à peu près au code de Panos Theof, et pour certaines choses qui peuvent déjà être suffisantes:
Comme vous pouvez le voir, cela est basé sur le doublement répété de la partie déjà initialisée. C'est simple et efficace, mais cela va à l'encontre des architectures de mémoire modernes. D'où est née une version qui n'utilise le doublage que pour créer un bloc de départ compatible avec le cache, qui est ensuite dynamité de manière itérative sur la zone cible:
Remarque: le code précédent devait
(count + 1) >> 1
servir de limite à la boucle de doublage pour garantir que l'opération de copie finale contienne suffisamment de fourrage pour couvrir tout ce qui reste. Ce ne serait pas le cas pour les dénombrements impairs sicount >> 1
devaient être utilisés à la place. Pour la version actuelle, cela n'a pas d'importance car la boucle de copie linéaire va prendre le moindre mou.La taille d'une cellule de tableau doit être transmise en tant que paramètre car - l'esprit boggle - les génériques ne sont pas autorisés à utiliser
sizeof
sauf s'ils utilisent une contrainte (unmanaged
) qui peut ou non devenir disponible à l'avenir. Des estimations erronées ne sont pas un gros problème, mais les performances sont meilleures si la valeur est exacte, pour les raisons suivantes:Sous-estimer la taille de l'élément peut entraîner des tailles de bloc supérieures à la moitié du cache L1, augmentant ainsi la probabilité que les données de la source de copie soient expulsées de L1 et doivent être récupérées à partir de niveaux de cache plus lents.
La surestimation de la taille de l'élément entraîne une sous-utilisation du cache L1 du processeur, ce qui signifie que la boucle de copie de bloc linéaire est exécutée plus souvent qu'elle ne le serait avec une utilisation optimale. Ainsi, la surcharge de boucle / appel fixe est engagée plus que strictement nécessaire.
Voici une référence comparant mon code
Array.Clear
et les trois autres solutions mentionnées précédemment. Les timings sont pour remplir des tableaux entiers (Int32[]
) des tailles données. Afin de réduire la variation causée par les caprices du cache, etc., chaque test a été exécuté deux fois de suite et les temporisations ont été prises pour la deuxième exécution.Si les performances de ce code ne sont pas suffisantes, une avenue prometteuse serait de paralléliser la boucle de copie linéaire (avec tous les threads utilisant le même bloc source), ou notre bon vieil ami P / Invoke.
Remarque: l'effacement et le remplissage des blocs sont normalement effectués par des routines d'exécution qui se ramifient en code hautement spécialisé à l'aide d'instructions MMX / SSE et ainsi de suite, donc dans tout environnement décent, on appellerait simplement l'équivalent moral respectif
std::memset
et serait assuré de niveaux de performance professionnels. IOW, de plein droit, la fonction de bibliothèqueArray.Clear
devrait laisser toutes nos versions roulées à la main dans la poussière. Le fait que ce soit l'inverse montre à quel point les choses sont vraiment éloignées. Il en va de même pour avoir à rouler le sienFill<>
en premier lieu, car il n'est toujours que dans Core et Standard mais pas dans le Framework. .NET existe depuis près de vingt ans maintenant et nous devons encore P / Invoke gauche et droite pour les choses les plus élémentaires ou rouler les nôtres ...la source
Il y a d'autres réponses à cette question (en double?): Quel est l'équivalent de memset en C #?
Quelqu'un a comparé les alternatives (elles incluaient une version dangereuse, mais elles n'ont pas essayé
memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.htmlla source
Voici une autre approche avec
System.Collections.BitArray
laquelle a un tel constructeur.ou
la source
Créez une classe privée à l'intérieur de laquelle vous créez le tableau et disposez d'un getter et d'un setter pour celui-ci. Sauf si vous avez besoin que chaque position du tableau soit unique, comme aléatoire, utilisez int? en tant que tableau, puis obtenez si la position est égale à null, remplissez cette position et retournez la nouvelle valeur aléatoire.
Ou utiliser
Comme setter.
la source
la source