Sé que las matrices instanciadas de tipos de valor en C # se rellenan automáticamente con el valor predeterminado del tipo (por ejemplo, falso para bool, 0 para int, etc.).
¿Hay alguna manera de rellenar automáticamente una matriz con un valor inicial que no sea el predeterminado? ¿Ya sea en la creación o en un método incorporado después (como Arrays.fill () de Java )? Digamos que quería una matriz booleana que fuera verdadera por defecto, en lugar de falsa. ¿Hay una forma integrada de hacer esto, o solo tienes que recorrer la matriz con un bucle 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;
}
Tener que iterar a través de la matriz y "restablecer" cada valor a verdadero parece ineficaz. ¿Hay alguna forma de evitar esto? ¿Quizás cambiando todos los valores?
Después de escribir esta pregunta y pensar en ello, supongo que los valores predeterminados son simplemente el resultado de cómo C # maneja la asignación de memoria de estos objetos detrás de escena, así que imagino que probablemente no sea posible hacer esto. ¡Pero todavía me gustaría saberlo con certeza!
fuente
Respuestas:
No conozco un método de marco, pero podría escribir un ayudante rápido para que lo haga por usted.
fuente
int[] arr = new int[16].Populate(-1);
void
aT[]
y luego puede hacerlovar a = new int[100].Polupate(1)
fuente
Enumerable.ToArray
no conoce el tamaño de la secuencia enumerable, por lo que tiene que adivinar el tamaño de la matriz. Eso significa que obtendrá asignaciones de matriz cada vezToArray
que se exceda el búfer, más una asignación más al final para el recorte. También hay gastos generales relacionados con el objeto enumerable.Cree una nueva matriz con mil
true
valores:Del mismo modo, puede generar secuencias enteras:
fuente
Para matrices grandes o matrices que serán de tamaño variable, probablemente debería usar:
Para una matriz pequeña, puede usar la sintaxis de inicialización de colección en C # 3:
El beneficio de la sintaxis de inicialización de la colección es que no tiene que usar el mismo valor en cada ranura y puede usar expresiones o funciones para inicializar una ranura. Además, creo que evita el costo de inicializar la ranura de la matriz al valor predeterminado. Así por ejemplo:
fuente
float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Si su matriz es tan grande, debe usar BitArray. Utiliza 1 bit para cada bool en lugar de un byte (como en una matriz de bools), también puede establecer todos los bits en true con operadores de bits. O simplemente inicializar en verdadero. Si solo necesitas hacerlo una vez, solo costará más.
fuente
Puede usar
Array.Fill
en .NET Core 2.0+ y .NET Standard 2.1+.fuente
desafortunadamente no creo que haya una forma directa, sin embargo, creo que puedes escribir un método de extensión para que la clase de matriz haga esto
fuente
Bueno, después de buscar un poco más en Google y leer, encontré esto:
Que sin duda está más cerca de lo que estoy buscando. Pero no estoy seguro si eso es mejor que iterar a través de la matriz original en un ciclo for y simplemente cambiar los valores. Después de una prueba rápida, de hecho, parece más lento en un factor de 5. ¡Entonces no es realmente una buena solución!
fuente
¿Qué pasa con una implementación paralela?
Cuando solo se inicializa una matriz, no se puede ver el poder de este código, pero creo que definitivamente debes olvidarte de lo "puro".
fuente
El siguiente código combina una iteración simple para copias pequeñas y Array. Copie para copias grandes
Los puntos de referencia para diferentes longitudes de matriz usando una matriz int [] son:
Las primeras columnas son el tamaño de la matriz, seguido del tiempo de copia mediante una iteración simple (implementación @JaredPared). El tiempo de este método es después de eso. Estos son los puntos de referencia que utilizan una matriz de una estructura de cuatro enteros
fuente
O ... simplemente podrías usar lógica invertida. Dejar
false
significartrue
y viceversa.Muestra de código
fuente
bool[] isVisible
hacerlobool[] isHidden
esto también funciona ... pero podría ser innecesario
fuente
Muchas de las respuestas presentadas aquí se reducen a un ciclo que inicializa la matriz un elemento a la vez, que no aprovecha las instrucciones de la CPU diseñadas para operar en un bloque de memoria a la vez.
.Net Standard 2.1 (en versión preliminar a partir de este escrito) proporciona Array.Fill () , que se presta a una implementación de alto rendimiento en la biblioteca de tiempo de ejecución (aunque, a partir de ahora, .NET Core no parece aprovechar esa posibilidad) .
Para aquellos en plataformas anteriores, el siguiente método de extensión supera a un bucle trivial por un margen sustancial cuando el tamaño de la matriz es significativo. Lo creé cuando mi solución para un desafío de código en línea era alrededor del 20% sobre el presupuesto de tiempo asignado. Redujo el tiempo de ejecución en aproximadamente un 70%. En este caso, el relleno de la matriz se realizó dentro de otro bucle. BLOCK_SIZE se estableció por instinto en lugar de experimentar. Algunas optimizaciones son posibles (por ejemplo, copiar todos los bytes ya establecidos en el valor deseado en lugar de un bloque de tamaño fijo).
fuente
Si planea establecer solo algunos de los valores en la matriz, pero desea obtener el valor predeterminado (personalizado) la mayor parte del tiempo, puede intentar algo como esto:
Probablemente necesitará implementar otras interfaces para que sea útil, como las de la matriz .
fuente
No hay forma de establecer todos los elementos en una matriz como una sola operación, A MENOS, ese valor es el valor predeterminado de los tipos de elementos.
Por ejemplo, si se trata de una matriz de enteros, puede establecerlos en cero con una sola operación, de esta manera:
Array.Clear(...)
fuente
Me doy cuenta de que llego tarde a la fiesta, pero aquí hay una idea. Escriba un contenedor que tenga operadores de conversión hacia y desde el valor empaquetado para que pueda usarse como un sustituto para el tipo empaquetado. En realidad, esto se inspiró en la respuesta tonta de @ l33t.
Primero (viniendo de C ++) me di cuenta de que en C # no se llama a un ctor predeterminado cuando se construyen los elementos de una matriz. En cambio, ¡incluso en presencia de un constructor predeterminado definido por el usuario! - Todos los elementos de la matriz están inicializados en cero. Eso me sorprendió.
Por lo tanto, una clase de contenedor que simplemente proporciona un ctor predeterminado con el valor deseado funcionaría para matrices en C ++ pero no en C #. Una solución alternativa es dejar que el tipo de contenedor se asigne 0 al valor inicial deseado después de la conversión. De esa manera, los valores inicializados cero parecen inicializarse con la semilla para todos los fines prácticos:
Este patrón es aplicable a todos los tipos de valores. Uno podría, por ejemplo, asignar de 0 a 4 para las entradas si se desea la inicialización con 4, etc.
Me encantaría crear una plantilla como sería posible en C ++, proporcionando el valor de inicialización como parámetro de plantilla, pero entiendo que eso no es posible en C #. ¿O me estoy perdiendo algo? (Por supuesto, en mapeo C ++ no es necesario en absoluto porque uno puede proporcionar un ctor predeterminado que se llamará para elementos de matriz).
FWIW, aquí hay un equivalente de C ++: https://ideone.com/wG8yEh .
fuente
Si puede invertir su lógica, puede usar el
Array.Clear()
método para establecer la matriz booleana en falso.fuente
Si está en .NET Core, .NET Standard> = 2.1, o depende del paquete System.Memory, también puede usar el
Span<T>.Fill()
método:https://dotnetfiddle.net/UsJ9bu
fuente
Aquí hay otra versión para nosotros, usuarios de Framework abandonados por Microsoft. Es 4 veces más rápido que
Array.Clear
y más rápido que la solución de Panos Theof y Eric J y una paralela de Petar Petrov - hasta dos veces más rápido para grandes matrices.Primero quiero presentarles el antepasado de la función, porque eso facilita la comprensión del código. En cuanto al rendimiento, esto está bastante a la par con el código de Panos Theof, y para algunas cosas que ya pueden ser suficientes:
Como puede ver, esto se basa en la duplicación repetida de la parte ya inicializada. Esto es simple y eficiente, pero está en conflicto con las arquitecturas de memoria modernas. Por lo tanto, nació una versión que usa la duplicación solo para crear un bloque de semillas amigable con la caché, que luego se lanza de forma iterativa sobre el área objetivo:
Nota: el código anterior era necesario
(count + 1) >> 1
como límite para el ciclo de duplicación para garantizar que la operación de copia final tenga suficiente forraje para cubrir todo lo que queda. Este no sería el caso para los recuentos impares sicount >> 1
se usaran en su lugar. Para la versión actual esto no tiene importancia ya que el bucle de copia lineal recogerá cualquier holgura.El tamaño de una celda de matriz debe pasarse como un parámetro porque, la mente alucina, no se permite el uso de genéricos a
sizeof
menos que usen una restricción (unmanaged
) que puede estar disponible o no en el futuro. Las estimaciones incorrectas no son un gran problema, pero el rendimiento es mejor si el valor es exacto, por las siguientes razones:Subestimar el tamaño del elemento puede dar lugar a tamaños de bloque superiores a la mitad de la memoria caché L1, lo que aumenta la probabilidad de que los datos de origen de la copia sean expulsados de L1 y tengan que volverse a buscar desde niveles de memoria caché más lentos.
Sobreestimar el tamaño del elemento da como resultado una infrautilización de la memoria caché L1 de la CPU, lo que significa que el bucle de copia en bloque lineal se ejecuta con más frecuencia de lo que sería con una utilización óptima. Por lo tanto, se incurre en una mayor sobrecarga de bucle / llamada fija de lo estrictamente necesario.
Aquí hay un punto de referencia que enfrenta mi código
Array.Clear
y las otras tres soluciones mencionadas anteriormente. Los tiempos son para llenar matrices enteras (Int32[]
) de los tamaños dados. Para reducir la variación causada por los caprichos de la memoria caché, etc., cada prueba se ejecutó dos veces, una tras otra, y los tiempos se tomaron para la segunda ejecución.Si el rendimiento de este código no es suficiente, una vía prometedora sería paralelizar el bucle de copia lineal (con todos los hilos utilizando el mismo bloque de origen), o nuestro buen viejo amigo P / Invoke.
Nota: la limpieza y el llenado de bloques normalmente se realiza mediante rutinas de tiempo de ejecución que se ramifican a código altamente especializado utilizando instrucciones MMX / SSE y demás, por lo que en cualquier entorno decente uno simplemente llamaría el equivalente moral respectivo
std::memset
y estaría seguro de los niveles de rendimiento profesional. IOW, por derecho, la función de bibliotecaArray.Clear
debería dejar todas nuestras versiones enrolladas a mano en el polvo. El hecho de que sea al revés muestra cuán fuera de control están realmente las cosas. Lo mismo ocurre con tener que rodar la propiaFill<>
en primer lugar, porque todavía está solo en Core y Standard, pero no en el Framework. .NET ha existido durante casi veinte años y todavía tenemos que P / Invocar de izquierda a derecha para las cosas más básicas o rodar el nuestro ...fuente
Hay algunas respuestas más sobre esta pregunta (¿duplicada?): ¿Cuál es el equivalente de memset en C #?
Alguien ha comparado las alternativas (incluyeron una versión insegura, pero no lo intentaron
memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.htmlfuente
Aquí hay otro appraoch con el
System.Collections.BitArray
que tiene tal constructor.o
fuente
Haga una clase privada en el interior donde cree la matriz y tenga un getter y setter para ello. A menos que necesite que cada posición en la matriz sea algo único, como aleatorio, use int? como una matriz y luego obtener si la posición es igual a nula, llenar esa posición y devolver el nuevo valor aleatorio.
O usar
Como acomodador.
fuente
fuente