Número de dígitos em um número inteiro em Java
1. Introdução
Neste tutorial rápido, exploraremosdifferent ways of getting the number of digits in an Integer em Java.
Também analisaremos esses métodos diferentes e descobriremos qual algoritmo se encaixa melhor em nossa situação.
2. Número de dígitos em umInteger
Para os métodos discutidos aqui, estamos considerando apenas números inteiros positivos. Se estamos esperando qualquer entrada negativa, podemos primeiro fazer uso deMath.abs(number) antes de usar qualquer um desses métodos.
2.1. Solução baseada emString
Talvez a maneira mais fácil de obter o número de dígitos em umInteger seja convertendo-o emString e chamando o métodolength(). Isso retornará o comprimento da representaçãoString do nosso número:
int length = String.valueOf(number).length();
But, this may be a sub-optimal approach, as this statement involves memory allocation for a String, for each evaluation. O JVM deve primeiro analisar nosso número e copiar seus dígitos em umString separado e realizar várias operações diferentes (como manter cópias temporárias, lidar com conversões Unicode, etc.).
Se tivermos apenas alguns números para avaliar, podemos seguir claramente essa solução - porque a diferença entre essa e qualquer outra abordagem será negligenciável, mesmo para grandes números.
2.2. Abordagem Logarítmica
Para os números representados na forma decimal, se pegarmos seu log na base 10 e arredondar para cima, obteremos o número de dígitos desse número:
int length = (int) (Math.log10(number) + 1);
Observe quelog100 de qualquer número não está definido. Portanto, se estamos esperando qualquer entrada com o valor0, podemos colocar uma verificação para isso também.
The logarithmic approach is significantly faster than String based approach as it doesn’t have to go through the process of any data conversion. Envolve apenas um cálculo simples e direto, sem qualquer inicialização de objeto extra ou loops.
2.3. Multiplicação Repetida
Neste método, pegaremos uma variável temporária (inicializada em 1) e a multiplicaremos continuamente por 10 até que se torne maior que nosso número. Durante este processo, também usaremos uma variávellength que manterá um registro do comprimento do número:
int length = 0;
long temp = 1;
while (temp <= number) {
length++;
temp *= 10;
}
return length;
Neste código, a linhatemp *= 10 é igual a escrevertemp = (temp << 3) + (temp << 1). Como a multiplicação geralmente é uma operação mais cara em alguns processadores quando comparada aos operadores de turnos, o último pode ser um pouco mais eficiente.
2.4. Dividindo com poderes de dois
Se soubermos sobre o intervalo do nosso número, poderemos usar uma variação que reduzirá ainda mais nossas comparações. Este método divide o número por potências de dois (por exemplo, 1, 2, 4, 8 etc.):
Este método divide o número por potências de dois (por exemplo, 1, 2, 4, 8 etc.):
int length = 1;
if (number >= 100000000) {
length += 8;
number /= 100000000;
}
if (number >= 10000) {
length += 4;
number /= 10000;
}
if (number >= 100) {
length += 2;
number /= 100;
}
if (number >= 10) {
length += 1;
}
return length;
Aproveita o fato de que qualquer número pode ser representado pela adição de potências de 2. Por exemplo, 15 podem ser representados como 8 + 4 + 2 + 1, todos com potências de 2.
Para um número de 15 dígitos, estaríamos fazendo 15 comparações em nossa abordagem anterior, que reduzimos para apenas 4 nesse método.
2.5. Dividir e conquistar
Talvez sejathe bulkiest approach quando comparado a todos os outros descritos aqui, mas nem é preciso dizerthis one is the fastest porque não estamos realizando nenhum tipo de conversão, multiplicação, adição ou inicialização de objeto.
Obtemos nossa resposta em apenas três ou quatro afirmaçõesif simples:
if (number < 100000) {
if (number < 100) {
if (number < 10) {
return 1;
} else {
return 2;
}
} else {
if (number < 1000) {
return 3;
} else {
if (number < 10000) {
return 4;
} else {
return 5;
}
}
}
} else {
if (number < 10000000) {
if (number < 1000000) {
return 6;
} else {
return 7;
}
} else {
if (number < 100000000) {
return 8;
} else {
if (number < 1000000000) {
return 9;
} else {
return 10;
}
}
}
}
Semelhante à abordagem anterior, podemos usar esse método apenas se soubermos sobre o intervalo do nosso número.
3. avaliação comparativa
Agora que temos um bom entendimento das soluções potenciais, vamos agora fazer alguns benchmarking simples de todos os nossos métodos usandoJava Microbenchmark Harness (JMH).
A tabela a seguir mostra o tempo médio de processamento de cada operação (em nanossegundos):
Benchmark Mode Cnt Score Error Units
Benchmarking.stringBasedSolution avgt 200 32.736 ± 0.589 ns/op
Benchmarking.logarithmicApproach avgt 200 26.123 ± 0.064 ns/op
Benchmarking.repeatedMultiplication avgt 200 7.494 ± 0.207 ns/op
Benchmarking.dividingWithPowersOf2 avgt 200 1.264 ± 0.030 ns/op
Benchmarking.divideAndConquer avgt 200 0.956 ± 0.011 ns/op
A solução baseada emString, que é a mais simples, é também a operação mais cara - por ser a única que requer conversão de dados e inicialização de novos objetos.
A abordagem logarítmica é significativamente mais eficiente, em comparação com a solução anterior - uma vez que não envolve nenhuma conversão de dados. E, por ser uma solução unifilar, pode ser uma boa alternativa para a abordagem baseada emString-.
A multiplicação repetida envolve multiplicação simples, proporcionalmente ao comprimento do número; por exemplo, se um número tiver quinze dígitos, esse método envolverá quinze multiplicações.
No entanto, o método seguinte tira vantagem do fato de que cada número pode ser representado por potências de dois (a abordagem semelhante ao BCD) e reduz o mesmo para 4 operações de divisão, por isso é ainda mais eficiente do que o anterior.
Finalmente, como podemos inferir,the most efficient algorithm is the verbose Divide and Conquer implementation - que fornece a resposta em apenas três ou quatro declarações if simples. Podemos usá-lo se tivermos um grande conjunto de dados de números que precisamos analisar.
4. Conclusão
Neste breve artigo, descrevemos algumas das maneiras de encontrar o número de dígitos em umIntegere comparamos a eficiência de cada abordagem.
E, como sempre, você pode encontrar o código completoover on GitHub.