Eu sei que matrizes instanciadas de tipos de valor em C # são preenchidas automaticamente com o valor padrão do tipo (por exemplo, false para bool, 0 para int, etc.).
Existe uma maneira de preencher automaticamente uma matriz com um valor inicial que não é o padrão? Na criação ou em um método interno posteriormente (como Arrays.fill () do Java )? Digamos que eu quisesse uma matriz booleana que fosse verdadeira por padrão, em vez de falsa. Existe uma maneira interna de fazer isso ou você apenas precisa percorrer a matriz com um loop 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;
}
Ter que percorrer a matriz e "redefinir" cada valor para true parece ineficaz. Existe alguma maneira de contornar isso? Talvez lançando todos os valores?
Depois de digitar essa questão e pensar sobre isso, acho que os valores padrão são simplesmente o resultado de como o C # lida com a alocação de memória desses objetos nos bastidores, então imagino que provavelmente não seja possível fazer isso. Mas eu ainda gostaria de ter certeza!
fonte
Respostas:
Não conhece um método de estrutura, mas você pode escrever um auxiliar rápido para fazer isso por você.
fonte
int[] arr = new int[16].Populate(-1);
void
paraT[]
e, em seguida, você pode fazervar a = new int[100].Polupate(1)
fonte
Enumerable.ToArray
não sabe o tamanho da sequência enumerável, por isso precisa adivinhar o tamanho da matriz. Isso significa que você obterá alocações de matriz sempre queToArray
o buffer for excedido, mais uma alocação no final para o corte. Também há sobrecarga envolvida com o objeto enumerável.Crie uma nova matriz com mil
true
valores:Da mesma forma, você pode gerar sequências inteiras:
fonte
Para matrizes grandes ou matrizes de tamanho variável, você provavelmente deve usar:
Para matriz pequena, você pode usar a sintaxe de inicialização da coleção em C # 3:
O benefício da sintaxe de inicialização da coleção é que você não precisa usar o mesmo valor em cada slot e pode usar expressões ou funções para inicializar um slot. Além disso, acho que você evita o custo de inicializar o slot da matriz para o valor padrão. Então, por exemplo:
fonte
float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Se sua matriz é tão grande, você deve usar o BitArray. Ele usa 1 bit para cada bool, em vez de um byte (como em uma matriz de bools). Você também pode definir todos os bits para true com os operadores de bit. Ou apenas inicialize em true. Se você precisar fazer apenas uma vez, isso custará apenas mais.
fonte
Você pode usar
Array.Fill
no .NET Core 2.0+ e no .NET Standard 2.1+.fonte
infelizmente não acho que exista uma maneira direta, porém acho que você pode escrever um método de extensão para a classe array fazer isso
fonte
Bem, depois de pesquisar um pouco mais no Google e ler, encontrei o seguinte:
O que certamente está mais próximo do que estou procurando. Mas não tenho certeza se isso é melhor do que percorrer a matriz original em um loop for e apenas alterar os valores. Depois de um teste rápido, ele parece mais lento em um fator de 5. Portanto, não é realmente uma boa solução!
fonte
Que tal uma implementação paralela
Ao inicializar apenas uma matriz, o poder desse código não pode ser visto, mas acho que você definitivamente deve esquecer o "puro" para.
fonte
O código abaixo combina iteração simples para cópias pequenas e Array.Copy para cópias grandes
Os benchmarks para diferentes comprimentos de matriz usando uma matriz int [] são:
As primeiras colunas são o tamanho da matriz, seguido pelo tempo de cópia usando uma iteração simples (implementação @JaredPared). O tempo desse método é depois disso. Estes são os benchmarks usando uma matriz de uma estrutura de quatro números inteiros
fonte
Ou ... você pode simplesmente usar lógica invertida. Vamos
false
significartrue
e vice-versa.Amostra de código
fonte
bool[] isVisible
fazer issobool[] isHidden
isso também funciona ... mas pode ser desnecessário
fonte
Muitas das respostas apresentadas aqui se resumem a um loop que inicializa a matriz um elemento por vez, o que não tira proveito das instruções da CPU projetadas para operar em um bloco de memória de uma só vez.
O .Net Standard 2.1 (em pré-visualização até este momento) fornece Array.Fill () , que se presta a uma implementação de alto desempenho na biblioteca de tempo de execução (embora, a partir de agora, o .NET Core não pareça aproveitar essa possibilidade) .
Para aqueles em plataformas anteriores, o método de extensão a seguir supera um loop trivial por uma margem substancial quando o tamanho da matriz é significativo. Eu o criei quando minha solução para um desafio de código on-line era cerca de 20% acima do orçamento de tempo alocado. Reduziu o tempo de execução em cerca de 70%. Nesse caso, o preenchimento da matriz foi realizado dentro de outro loop. BLOCK_SIZE foi definido pelo instinto em vez de pelo experimento. Algumas otimizações são possíveis (por exemplo, copiar todos os bytes já configurados para o valor desejado em vez de um bloco de tamanho fixo).
fonte
Se você planeja definir apenas alguns valores na matriz, mas deseja obter o valor padrão (personalizado) na maior parte do tempo, tente algo como:
Você provavelmente precisará implementar outras interfaces para torná-lo útil, como as da própria matriz .
fonte
Não há como definir todos os elementos em uma matriz como uma única operação, a menos que esse valor seja o valor padrão do tipo de elemento.
Por exemplo, se for uma matriz de números inteiros, você poderá configurá-los para zero com uma única operação, da seguinte forma:
Array.Clear(...)
fonte
Sei que estou atrasada para a festa, mas aqui está uma idéia. Escreva um wrapper que possua operadores de conversão de e para o valor agrupado, para que possa ser usado como substituto do tipo agrupado. Na verdade, isso foi inspirado pela resposta boba da @ l33t.
Primeiro (vindo de C ++), percebi que em C # um ctor padrão não é chamado quando os elementos de uma matriz são construídos. Em vez disso - mesmo na presença de um construtor padrão definido pelo usuário! - todos os elementos da matriz são inicializados com zero. Isso me surpreendeu.
Portanto, uma classe de wrapper que simplesmente fornece um ctor padrão com o valor desejado funcionaria para matrizes em C ++, mas não em C #. Uma solução alternativa é permitir que o tipo de invólucro mapeie 0 para o valor inicial desejado na conversão. Dessa forma, os valores inicializados zero parecem ser inicializados com a semente para todos os fins práticos:
Esse padrão é aplicável a todos os tipos de valor. Pode-se, por exemplo, mapear 0 a 4 para ints se a inicialização com 4 for desejada etc.
Eu adoraria fazer um modelo dele como seria possível em C ++, fornecendo o valor inicial como parâmetro de modelo, mas entendo que isso não é possível em C #. Ou eu estou esquecendo de alguma coisa? (É claro que o mapeamento em C ++ não é necessário, porque é possível fornecer um ctor padrão que será chamado para elementos de matriz.)
FWIW, aqui está um equivalente em C ++: https://ideone.com/wG8yEh .
fonte
Se você pode inverter sua lógica, pode usar o
Array.Clear()
método para definir a matriz booleana como false.fonte
Se você estiver no .NET Core, .NET Standard> = 2.1, ou depender do pacote System.Memory, também poderá usar o
Span<T>.Fill()
método:https://dotnetfiddle.net/UsJ9bu
fonte
Aqui está outra versão para nós, usuários do Framework abandonados pela Microsoft. É 4 vezes mais rápido que
Array.Clear
e mais rápido do que a solução da Panos Theof e Eric J de e uma paralela do Petar Petrov - até duas vezes mais rápido para grandes matrizes.Primeiro, quero apresentar o ancestral da função, porque isso facilita a compreensão do código. Em termos de desempenho, isso é parecido com o código de Panos Theof, e para algumas coisas que já podem ser suficientes:
Como você pode ver, isso se baseia na duplicação repetida da parte já inicializada. Isso é simples e eficiente, mas está em conflito com as arquiteturas de memória modernas. Portanto, nasceu uma versão que usa a duplicação apenas para criar um bloco de propagação compatível com cache, que é distribuído iterativamente pela área de destino:
Nota: o código anterior necessário
(count + 1) >> 1
como limite para o ciclo de duplicação para garantir que a operação de cópia final tenha forragem suficiente para cobrir tudo o que resta. Esse não seria o caso de contagens ímpares, secount >> 1
fosse utilizado em seu lugar. Para a versão atual, isso não tem significado, uma vez que o loop de cópia linear receberá qualquer folga.O tamanho de uma célula de matriz deve ser passado como parâmetro porque - a mente desconcerta - os genéricos não podem usar, a
sizeof
menos que usem uma restrição (unmanaged
) que pode ou não estar disponível no futuro. Estimativas erradas não são um grande problema, mas o desempenho é melhor se o valor for preciso, pelos seguintes motivos:Subestimar o tamanho do elemento pode levar a tamanhos de bloco maiores que a metade do cache L1, aumentando a probabilidade de os dados da fonte de cópia serem despejados do L1 e precisando ser buscados novamente a partir de níveis mais lentos do cache.
Superestimar o tamanho do elemento resulta na subutilização do cache L1 da CPU, o que significa que o loop de cópia de bloco linear é executado com mais frequência do que seria com a utilização ideal. Assim, ocorre mais sobrecarga de loop / chamada fixa do que o estritamente necessário.
Aqui está uma referência comparando meu código
Array.Clear
e as outras três soluções mencionadas anteriormente. Os horários são para o preenchimento de matrizes inteiras (Int32[]
) dos tamanhos fornecidos. Para reduzir a variação causada por caprichos no cache, etc., cada teste foi executado duas vezes, consecutivamente, e os tempos foram tomados para a segunda execução.Se o desempenho desse código não for suficiente, um caminho promissor seria paralelo ao loop de cópia linear (com todos os threads usando o mesmo bloco de origem) ou ao nosso bom e velho amigo P / Invoke.
Nota: a limpeza e o preenchimento de blocos normalmente são feitos por rotinas de tempo de execução que ramificam para código altamente especializado usando instruções MMX / SSE e outras coisas, portanto, em qualquer ambiente decente, basta chamar o respectivo equivalente moral
std::memset
e garantir níveis de desempenho profissional. IOW, por direitos, a função de bibliotecaArray.Clear
deve deixar todas as nossas versões enroladas à mão na poeira. O fato de ser o contrário mostra o quão longe as coisas estão realmente. O mesmo vale para ter que rolar a si próprioFill<>
em primeiro lugar, porque ainda está apenas no Core e no Standard, mas não no Framework. O .NET já existe há quase vinte anos e ainda temos que P / Invocar para a esquerda e para a direita para obter as coisas mais básicas ou ...fonte
Há mais algumas respostas sobre essa pergunta (duplicada?): Qual é o equivalente do memset em C #?
Alguém comparou as alternativas (elas incluíram uma versão insegura, mas não tentaram
memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.htmlfonte
Aqui está outra abordagem com a
System.Collections.BitArray
qual tem esse construtor.ou
fonte
Faça uma classe privada dentro de onde você faz a matriz e tenha um getter e setter para ela. A menos que você precise que cada posição na matriz seja algo único, como aleatório, use int? como uma matriz e, em seguida, obtenha se a posição for igual a nula, preencha essa posição e retorne o novo valor aleatório.
Ou use
Como setter.
fonte
fonte