Desde o Java 5, tivemos o boxe / unboxing de tipos primitivos, de modo que int
seja moldado para ser java.lang.Integer
, e assim por diante.
Eu vejo um monte de novos projetos Java recentemente (que definitivamente necessário um JRE de pelo menos a versão 5, se não 6) que está usando int
, em vez de java.lang.Integer
, embora seja muito mais conveniente para usar o último, já que tem alguns métodos auxiliares para a conversão para long
valores et al.
Por que alguns ainda usam tipos primitivos em Java? Existe algum benefício tangível?
java
primitive
primitive-types
autoboxing
jdk1.5
Naftuli Kay
fonte
fonte
new IntegeR(5) == new Integer(5)
, segundo as regras, avalie como falso.Respostas:
No Java efetivo de Joshua Bloch , item 5: "Evite criar objetos desnecessários", ele publica o seguinte exemplo de código:
e leva 43 segundos para ser executado. Levar o Long até o primitivo reduz para 6,8 segundos ... Se isso é alguma indicação de por que usamos primitivos.
A falta de igualdade de valor nativo também é uma preocupação (
.equals()
é bastante detalhada em comparação com==
)para biziclop:
Resulta em:
EDITAR Por que (3) retorna
true
e (4) retornafalse
?Porque eles são dois objetos diferentes. Os 256 números inteiros mais próximos de zero [-128; 127] são armazenados em cache pela JVM, portanto, eles retornam o mesmo objeto para eles. Além desse intervalo, porém, eles não são armazenados em cache, portanto, um novo objeto é criado. Para tornar as coisas mais complicadas, o JLS exige que pelo menos 256 flyweights sejam armazenados em cache. Os implementadores da JVM podem adicionar mais, se desejarem, o que significa que isso pode ser executado em um sistema em que os 1024 mais próximos são armazenados em cache e todos eles retornam verdadeiros ... #awkward
fonte
i
foram declaradosLong
!==
operador execute comparações de identidade de referência emInteger
expressões e comparações de igualdade de valor emint
expressões.Integer.equals()
existe por esse mesmo motivo. Você nunca deve usar==
para comparar valores em qualquer tipo não primitivo. Esta é Java 101.O autounboxing pode levar a NPEs difíceis de detectar
Na maioria das situações, a atribuição nula a
in
é muito menos óbvia do que acima.fonte
Os tipos in a box apresentam desempenho inferior e requerem mais memória.
fonte
Tipos primitivos:
Agora avalie:
É
true
. Dificilmente surpreendente. Agora tente os tipos de caixa:Agora avalie:
É
false
. Provavelmente. Depende do tempo de execução. Essa razão é suficiente?fonte
Além de problemas de desempenho e memória, eu gostaria de apresentar outro problema: a
List
interface seria quebrada semint
.O problema é o
remove()
método sobrecarregado (remove(int)
vs.remove(Object)
).remove(Integer)
sempre resolveria chamar o último, para que você não pudesse remover um elemento pelo índice.Por outro lado, há uma armadilha ao tentar adicionar e remover um
int
:fonte
Vector
teveremoveElementAt(int)
desde o início.remove(int)
foi introduzido com a estrutura de coleções em Java 1.2.List
API foi projetada, nem os Genéricos nem o Autoboxing existiam, então não havia chance de se misturarremove(int)
eremove(Object)
...Você pode realmente imaginar um
loop com java.lang.Integer vez? Um java.lang.Integer é imutável, portanto, cada incremento ao redor do loop criaria um novo objeto java no heap, em vez de apenas incrementar o int na pilha com uma única instrução JVM. O desempenho seria diabólico.
Eu realmente discordo que é muito mais conveniente usar o java.lang.Integer do que int. Pelo contrário. Autoboxing significa que você pode usar int onde, caso contrário, seria forçado a usar Inteiro, e o compilador java se encarrega de inserir o código para criar o novo objeto Inteiro para você. Autoboxing é sobre permitir que você use um int onde um número inteiro é esperado, com o compilador inserindo a construção do objeto relevante. De maneira alguma remove ou reduz a necessidade do int em primeiro lugar. Com o autobox, você obtém o melhor dos dois mundos. Você cria um número inteiro automaticamente para você quando precisa de um objeto java baseado em heap e obtém a velocidade e a eficiência de um int quando está apenas fazendo cálculos aritméticos e locais.
fonte
Tipos primitivos são muito mais rápidos:
Inteiro (todos os números e também uma String) é um tipo imutável : uma vez criado, não pode ser alterado. Se
i
fosse Inteiro,i++
isso criaria um novo objeto Inteiro - muito mais caro em termos de memória e processador.fonte
i++
em outra variável; portanto, o Inteiro precisa ser imutável para poder fazer isso (ou pelo menos issoi++
teria que criar um novo objeto Inteiro de qualquer maneira). (E os valores primitivos também são imutáveis - você simplesmente não comenta isso, pois eles não são objetos.)++
é um arenque vermelho aqui. Imagine o Java foi aprimorado para operador de suporte a sobrecarga de uma forma muito simples, de tal forma que se uma classe (comoInteger
tem um métodoplus
, então você poderia escreveri + 1
em vez dei.plus(1)
. E assumem também que o compilador é inteligente o suficiente para expandiri++
emi = i + 1
. Agora você poderia dizeri++
e eficaz "incrementar a variável i" semInteger
ser mutável.Em primeiro lugar, hábito. Se você codifica em Java há oito anos, acumula uma quantidade considerável de inércia. Por que mudar se não há motivo convincente para fazê-lo? Não é como se o uso de primitivas in a box tivesse vantagens extras.
O outro motivo é afirmar que
null
não é uma opção válida. Seria inútil e enganoso declarar a soma de dois números ou uma variável de loop comoInteger
.Também há o aspecto de desempenho, embora a diferença de desempenho não seja crítica em muitos casos (embora seja muito ruim), ninguém gosta de escrever código que possa ser escrito com a mesma facilidade e rapidez que já estamos. costumava.
fonte
A propósito, o Smalltalk possui apenas objetos (sem primitivos) e, no entanto, eles haviam otimizado seus inteiros pequenos (usando nem todos os 32 bits, apenas 27 ou mais) para não alocar espaço de heap, mas simplesmente usando um padrão de bits especial. Também outros objetos comuns (verdadeiro, falso, nulo) tinham padrões de bits especiais aqui.
Portanto, pelo menos nas JVMs de 64 bits (com um espaço de nome do ponteiro de 64 bits), deve ser possível não ter nenhum objeto inteiro, caractere, byte, curto, booleano, flutuante (e pequeno longo) (exceto os criados por explícito
new ...()
), apenas padrões de bits especiais, que poderiam ser manipulados pelos operadores normais com bastante eficiência.fonte
Não acredito que ninguém tenha mencionado o que considero o motivo mais importante: "int" é muito mais fácil de digitar do que "Inteiro". Eu acho que as pessoas subestimam a importância de uma sintaxe concisa. O desempenho não é realmente um motivo para evitá-los, porque na maioria das vezes quando se usa números estão em índices de loop, e incrementar e comparar esses custos não custa nada em um loop não trivial (se você estiver usando int ou Inteiro).
O outro motivo foi que você pode obter NPEs, mas isso é extremamente fácil de evitar com tipos de caixa (e é garantido que será evitado desde que você sempre os inicialize com valores não nulos).
A outra razão foi que (novo Long (1000)) == (novo Long (1000)) é falso, mas essa é apenas outra maneira de dizer que ".equals" não tem suporte sintático para tipos de caixa (ao contrário dos operadores <,> , =, etc), então voltamos ao motivo da "sintaxe mais simples".
Acho que o exemplo de loop não primitivo de Steve Yegge ilustra muito bem meu argumento: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb
Pense sobre isso: com que freqüência você usa tipos de função em linguagens que possuem boa sintaxe para eles (como qualquer linguagem funcional, python, ruby e até C) em comparação com java, onde é necessário simulá-los usando interfaces como Runnable e Callable e classes sem nome.
fonte
Algumas razões para não se livrar das primitivas:
Se for eliminado, qualquer programa antigo nem será executado.
A JVM inteira teria que ser reescrita para suportar essa coisa nova.
Você precisaria armazenar o valor e a referência, que usa mais memória. Se você possui uma grande variedade de bytes, o uso
byte
de é significativamente menor do que o usoByte
de.Declarar
int i
que fazer coisas comi
isso resultaria em nenhum problema, mas declararInteger i
e fazer o mesmo resultaria em um NPE.Considere este código:
Seria falso. Os operadores teriam que estar sobrecarregados, e isso resultaria em uma grande reescrita das coisas.
Os wrappers de objetos são significativamente mais lentos que seus equivalentes primitivos.
fonte
Os objetos são muito mais pesados que os tipos primitivos; portanto, os tipos primitivos são muito mais eficientes que as instâncias de classes de wrapper.
Os tipos primitivos são muito simples: por exemplo, um int é de 32 bits, ocupa exatamente 32 bits de memória e pode ser manipulado diretamente. Um objeto Inteiro é um objeto completo, que (como qualquer objeto) deve ser armazenado no heap e só pode ser acessado através de uma referência (ponteiro) a ele. Provavelmente, também ocupa mais de 32 bits (4 bytes) de memória.
Dito isto, o fato de Java fazer uma distinção entre tipos primitivos e não primitivos também é um sinal de idade da linguagem de programação Java. As linguagens de programação mais recentes não têm essa distinção; o compilador dessa linguagem é inteligente o suficiente para descobrir por si mesmo se você estiver usando valores simples ou objetos mais complexos.
Por exemplo, em Scala não há tipos primitivos; existe uma classe Int para números inteiros e um Int é um objeto real (no qual você pode usar métodos etc.). Quando o compilador compila seu código, ele usa entradas primitivas nos bastidores, portanto, usar um Int é tão eficiente quanto usar um int primitivo em Java.
fonte
Além do que outros disseram, variáveis locais primitivas não são alocadas do heap, mas na pilha. Mas os objetos são alocados da pilha e, portanto, precisam ser coletados como lixo.
fonte
Tipos primitivos têm muitas vantagens:
fonte
É difícil saber que tipo de otimizações estão acontecendo ocultamente.
Para uso local, quando o compilador tiver informações suficientes para fazer otimizações, excluindo a possibilidade do valor nulo, espero que o desempenho seja o mesmo ou semelhante .
No entanto, matrizes de primitivas são aparentemente muito diferentes das coleções de primitivas em caixa. Isso faz sentido, uma vez que poucas otimizações são possíveis dentro de uma coleção.
Além disso,
Integer
possui uma sobrecarga lógica muito maior em comparação comint
: agora você precisa se preocupar seint a = b + c;
lança ou não uma exceção.Eu usaria as primitivas o máximo possível e confiaria nos métodos de fábrica e no autoboxing para me fornecer os tipos de caixas semanticamente mais poderosos quando necessários.
fonte
Em uma nota lateral, eu não me importaria de ver algo assim descobrir o caminho para Java.
Onde o loop for incrementa automaticamente o loop1 de 0 a 1000 ou
Onde o loop for diminui automaticamente o loop1 1000 para 0.
fonte
Você deve perguntar por que o tipo de classe / objeto é necessário
A razão para ter um tipo de objeto é facilitar nossa vida quando lidamos com coleções. As primitivas não podem ser adicionadas diretamente à Lista / Mapa, em vez de você precisar escrever uma classe de wrapper. O tipo de classe Readymade Integer ajuda você aqui, além de ter muitos métodos utilitários como Integer.pareseInt (str)
fonte
Concordo com as respostas anteriores, o uso de objetos wrapper primitivos pode ser caro. Porém, se o desempenho não for crítico em seu aplicativo, você evitará estouros ao usar objetos. Por exemplo:
O valor de
bigNumber
é -2147483647 e você esperaria que fosse 2147483649. É um erro no código que seria corrigido ao fazer:E
bigNumber
seria 2147483649. Às vezes, esses tipos de bugs são fáceis de serem esquecidos e podem levar a comportamentos ou vulnerabilidades desconhecidas (consulte CWE-190 ).Se você usar objetos wrapper, o código equivalente não será compilado.
Portanto, é mais fácil interromper esse tipo de problema usando objetos de wrapper primitivos.
Sua pergunta já está tão respondida, que respondo apenas para adicionar um pouco mais de informações não mencionadas anteriormente.
fonte
Porque o JAVA executa todas as operações matemáticas em tipos primitivos. Considere este exemplo:
Aqui, as operações de lembrete e unário mais não podem ser aplicadas no tipo Inteiro (Referência), o compilador executa o unboxing e realiza as operações.
Portanto, verifique quantas operações de caixa automática e unboxing acontecem no programa java. Desde então, leva tempo para executar essas operações.
Geralmente, é melhor manter argumentos do tipo Reference e resultado do tipo primitivo.
fonte
Os tipos primitivos são muito mais rápidos e requerem muito menos memória . Portanto, podemos preferir usá-los.
Por outro lado, a especificação atual da linguagem Java não permite o uso de tipos primitivos nos tipos parametrizados (genéricos), nas coleções Java ou na API Reflection.
Quando nosso aplicativo precisa de coleções com um grande número de elementos, devemos considerar o uso de matrizes com o tipo mais "econômico" possível.
* Para informações detalhadas, consulte a fonte: https://www.baeldung.com/java-primitives-vs-objects
fonte
Para ser breve: os tipos primitivos são mais rápidos e requerem menos memória que os in a box
fonte