Desde Java 5, hemos tenido boxing / unboxing de tipos primitivos para que int
esté envuelto para ser java.lang.Integer
, y así sucesivamente.
Veo muchos nuevos proyectos Java últimamente (que definitivamente requieren un JRE de al menos la versión 5, si no la 6) que se usan en int
lugar de java.lang.Integer
, aunque es mucho más conveniente usar este último, ya que tiene algunos métodos auxiliares para convertir a long
valores et al.
¿Por qué algunos todavía usan tipos primitivos en Java? ¿Hay algún beneficio tangible?
java
primitive
primitive-types
autoboxing
jdk1.5
Naftuli Kay
fuente
fuente
new IntegeR(5) == new Integer(5)
, según las reglas, evaluar como falso.Respuestas:
En Java eficaz de Joshua Bloch , Elemento 5: "Evite crear objetos innecesarios", publica el siguiente ejemplo de código:
y tarda 43 segundos en ejecutarse. Tomar el Long en la primitiva lo reduce a 6.8 segundos ... Si eso es una indicación de por qué usamos primitivas.
La falta de igualdad de valor nativo también es una preocupación (
.equals()
es bastante detallada en comparación con==
)para biziclop:
Resultados en:
EDITAR ¿Por qué (3) regresa
true
y (4) regresafalse
?Porque son dos objetos diferentes. Los 256 enteros más cercanos a cero [-128; 127] son almacenados en caché por la JVM, por lo que devuelven el mismo objeto para ellos. Sin embargo, más allá de ese rango, no se almacenan en caché, por lo que se crea un nuevo objeto. Para hacer las cosas más complicadas, el JLS exige que se almacenen en caché al menos 256 pesos mosca. Los implementadores de JVM pueden agregar más si lo desean, lo que significa que esto podría ejecutarse en un sistema donde los 1024 más cercanos se almacenan en caché y todos devuelven verdadero ... #awkward
fuente
i
se declararanLong
!==
operador realiza comparaciones de identidad de referencia enInteger
expresiones y comparaciones de igualdad de valores enint
expresiones.Integer.equals()
existe por esta misma razón. Usted debe nunca se use==
para comparar los valores de cualquier tipo no primitivo. Esto es Java 101.El autounboxing puede conducir a NPE difíciles de detectar
En la mayoría de las situaciones, la asignación nula a
in
es mucho menos obvia que la anterior.fuente
Los tipos en caja tienen peor rendimiento y requieren más memoria.
fuente
Tipos primitivos:
Ahora evalúe:
Es
true
. Difícil de sorprender. Ahora prueba los tipos en caja:Ahora evalúe:
Es
false
. Probablemente. Depende del tiempo de ejecución. ¿Es esa razón suficiente?fuente
Además de los problemas de rendimiento y memoria, me gustaría plantear otro problema: la
List
interfaz se rompería sin ellaint
.El problema es el
remove()
método sobrecargado (remove(int)
vs.remove(Object)
).remove(Integer)
siempre resolvería llamar a este último, por lo que no podría eliminar un elemento por índice.Por otro lado, hay un error al intentar agregar y eliminar un
int
:fuente
Vector
teníaremoveElementAt(int)
desde el principio.remove(int)
se introdujo con el marco de colecciones en Java 1.2.List
se diseñó la API, no existían ni Generics ni Autoboxing, por lo que no había posibilidad de mezclarseremove(int)
yremove(Object)
...¿Realmente puedes imaginar un
bucle con java.lang.Integer en su lugar? Un java.lang.Integer es inmutable, por lo que cada incremento alrededor del ciclo crearía un nuevo objeto java en el montón, en lugar de simplemente incrementar el int en la pila con una sola instrucción JVM. El rendimiento sería diabólico.
Realmente no estoy de acuerdo con que es mucho más conveniente usar java.lang.Integer que int. De lo contrario. Autoboxing significa que puede usar int donde de otro modo se vería obligado a usar Integer, y el compilador de Java se encarga de insertar el código para crear el nuevo objeto Integer por usted. Autoboxing se trata de permitirle usar un int donde se espera un Integer, con el compilador insertando la construcción del objeto relevante. De ninguna manera elimina o reduce la necesidad de int en primer lugar. Con autoboxing obtienes lo mejor de ambos mundos. Obtiene un Entero creado automáticamente cuando necesita un objeto Java basado en el montón, y obtiene la velocidad y la eficiencia de un int cuando solo está haciendo cálculos aritméticos y locales.
fuente
Los tipos primitivos son mucho más rápidos:
El entero (todos los números y también una cadena) es un tipo inmutable : una vez creado, no se puede cambiar. Si
i
fuera Integer,i++
entonces crearía un nuevo objeto Integer, mucho más costoso en términos de memoria y procesador.fuente
i++
en otra variable, por lo que Integer necesita ser inmutable para poder hacer esto (o al menos estoi++
tendría que crear un nuevo objeto Integer de todos modos). (Y los valores primitivos son inmutables, también - sólo no comenta este ya que hay objetos.)++
es un arenque rojo aquí. Imagínese Java se ha mejorado para operador de soporte de la sobrecarga de una manera muy sencilla, de manera que si una clase (por ejemplo,Integer
tiene un métodoplus
, entonces usted podría escribiri + 1
en lugar dei.plus(1)
. Y suponer también que el compilador es suficientemente inteligente como para ampliari++
eni = i + 1
. Ahora se podría deciri++
y efectivamente "incrementar la variable i" sinInteger
ser mutable. "Ante todo, hábito. Si ha codificado en Java durante ocho años, acumula una cantidad considerable de inercia. ¿Por qué cambiar si no hay una razón convincente para hacerlo? No es que usar primitivas en caja tenga ventajas adicionales.
La otra razón es afirmar que
null
no es una opción válida. Sería inútil y engañoso declarar la suma de dos números o una variable de bucle comoInteger
.También está el aspecto del rendimiento, aunque la diferencia de rendimiento no es crítica en muchos casos (aunque cuando lo es, es bastante malo), a nadie le gusta escribir código que podría escribirse con la misma facilidad de una manera más rápida. Acostumbrado a.
fuente
Por cierto, Smalltalk solo tiene objetos (sin primitivas) y, sin embargo, han optimizado sus enteros pequeños (utilizando no todos los 32 bits, solo 27 o similares) para no asignar ningún espacio de almacenamiento dinámico, sino simplemente usar un patrón de bits especial. También otros objetos comunes (verdadero, falso, nulo) tenían patrones de bits especiales aquí.
Por lo tanto, al menos en JVM de 64 bits (con un espacio de nombres de puntero de 64 bits) debería ser posible no tener ningún objeto de entero, carácter, byte, corto, booleano, flotante (y pequeño largo) (aparte de estos creados por explícito
new ...()
), solo patrones de bits especiales, que podrían ser manipulados por los operadores normales con bastante eficiencia.fuente
No puedo creer que nadie haya mencionado lo que creo que es la razón más importante: "int" es mucho más fácil de escribir que "Integer". Creo que la gente subestima la importancia de una sintaxis concisa. El rendimiento no es realmente una razón para evitarlos porque la mayoría de las veces cuando uno usa números está en índices de bucle, e incrementar y comparar esos no cuesta nada en ningún bucle no trivial (ya sea que esté usando int o Integer).
La otra razón dada fue que puede obtener NPE, pero eso es extremadamente fácil de evitar con los tipos en caja (y se garantiza que se evitará siempre que los inicialice siempre a valores no nulos).
La otra razón fue que (new Long (1000)) == (new Long (1000)) es falso, pero esa es solo otra forma de decir que ".equals" no tiene soporte sintáctico para los tipos en caja (a diferencia de los operadores <,> , =, etc.), así que volvemos a la razón de "sintaxis más simple".
Creo que el ejemplo de bucle no primitivo de Steve Yegge ilustra muy bien mi punto: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb
Piense en esto: con qué frecuencia utiliza tipos de funciones en lenguajes que tienen una buena sintaxis para ellos (como cualquier lenguaje funcional, python, ruby e incluso C) en comparación con Java, donde debe simularlos usando interfaces como Runnable y Callable y clases sin nombre.
fuente
Un par de razones para no deshacerse de los primitivos:
Si se elimina, los programas antiguos ni siquiera se ejecutarían.
Toda la JVM tendría que reescribirse para admitir esta nueva cosa.
Necesitaría almacenar el valor y la referencia, que usa más memoria. Si tiene una gran variedad de bytes, usar
byte
's es significativamente más pequeño que usarByte
' s.Declarar
int i
luego hacer cosas con noi
generaría problemas, pero declararInteger i
y hacer lo mismo generaría un NPE.Considera este código:
Sería falso Los operadores tendrían que estar sobrecargados, y eso resultaría en una gran reescritura de cosas.
Los envoltorios de objetos son significativamente más lentos que sus homólogos primitivos.
fuente
Los objetos son mucho más pesados que los tipos primitivos, por lo que los tipos primitivos son mucho más eficientes que las instancias de clases de contenedor.
Los tipos primitivos son muy simples: por ejemplo, un int tiene 32 bits y ocupa exactamente 32 bits en la memoria, y puede manipularse directamente. Un objeto entero es un objeto completo, que (como cualquier objeto) debe almacenarse en el montón y solo se puede acceder a él a través de una referencia (puntero). Lo más probable es que también ocupe más de 32 bits (4 bytes) de memoria.
Dicho esto, el hecho de que Java tenga una distinción entre tipos primitivos y no primitivos también es un signo de la antigüedad del lenguaje de programación Java. Los lenguajes de programación más nuevos no tienen esta distinción; El compilador de dicho lenguaje es lo suficientemente inteligente como para descubrir por sí mismo si está utilizando valores simples u objetos más complejos.
Por ejemplo, en Scala no hay tipos primitivos; hay una clase Int para enteros, y un Int es un objeto real (que puedes usar métodos, etc.). Cuando el compilador compila su código, usa entradas primitivas detrás de escena, por lo que usar un Int es tan eficiente como usar un int primitivo en Java.
fuente
Además de lo que otros han dicho, las variables locales primitivas no se asignan desde el montón, sino en la pila. Pero los objetos se asignan desde el montón y, por lo tanto, deben ser recolectados.
fuente
Los tipos primitivos tienen muchas ventajas:
fuente
Es difícil saber qué tipo de optimizaciones están sucediendo debajo de las cubiertas.
Para uso local, cuando el compilador tiene suficiente información para realizar optimizaciones excluyendo la posibilidad del valor nulo, espero que el rendimiento sea el mismo o similar .
Sin embargo, las matrices de primitivas son aparentemente muy diferentes de las colecciones de primitivas en caja. Esto tiene sentido dado que muy pocas optimizaciones son posibles dentro de una colección.
Además,
Integer
tiene una sobrecarga lógica mucho mayor en comparación conint
: ahora debe preocuparse por siint a = b + c;
arroja una excepción o no .Usaría las primitivas tanto como fuera posible y confiaría en los métodos de fábrica y el autoboxing para darme los tipos en caja semánticamente más potentes cuando sean necesarios.
fuente
En una nota al margen, no me importaría ver algo como esto encontrar su camino en Java.
Donde el bucle for incrementa automáticamente el loop1 de 0 a 1000 o
Donde el bucle for disminuye automáticamente loop1 1000 a 0.
fuente
Debe preguntar por qué se requiere el tipo de clase / objeto
La razón para tener el tipo de objeto es hacernos la vida más fácil cuando tratamos con colecciones. Las primitivas no se pueden agregar directamente a Lista / Mapa, sino que debe escribir una clase de contenedor. El tipo de clases Readymade Integer lo ayuda aquí, además de que tiene muchos métodos de utilidad como Integer.pareseInt (str)
fuente
Estoy de acuerdo con las respuestas anteriores, el uso de objetos envoltorios primitivos puede ser costoso. Pero, si el rendimiento no es crítico en su aplicación, evite desbordamientos al usar objetos. Por ejemplo:
El valor de
bigNumber
es -2147483647, y es de esperar que sea 2147483649. Es un error en el código que se solucionaría haciendo:Y
bigNumber
sería 2147483649. Este tipo de errores a veces son fáciles de pasar por alto y pueden conducir a comportamientos desconocidos o vulnerabilidades (ver CWE-190 ).Si usa objetos de contenedor, el código equivalente no se compilará.
Por lo tanto, es más fácil detener este tipo de problemas mediante el uso de objetos envoltorios primitivos.
Su pregunta ya está tan respondida, que respondo solo para agregar un poco más de información no mencionada anteriormente.
fuente
Porque JAVA realiza todas las operaciones matemáticas en tipos primitivos. Considere este ejemplo:
Aquí, las operaciones de recordatorio y unario plus no se pueden aplicar en el tipo Entero (Referencia), el compilador realiza el desempaquetado y realiza las operaciones.
Por lo tanto, asegúrese de cuántas operaciones de autoboxing y unboxing ocurren en el programa java. Desde entonces, lleva tiempo realizar estas operaciones.
En general, es mejor mantener argumentos de tipo Referencia y resultado de tipo primitivo.
fuente
Los tipos primitivos son mucho más rápidos y requieren mucha menos memoria . Por lo tanto, podríamos preferir usarlos.
Por otro lado, la especificación actual del lenguaje Java no permite el uso de tipos primitivos en los tipos parametrizados (genéricos), en las colecciones de Java o la API Reflection.
Cuando nuestra aplicación necesita colecciones con una gran cantidad de elementos, deberíamos considerar el uso de matrices con el tipo más "económico" posible.
* Para obtener información detallada, consulte la fuente: https://www.baeldung.com/java-primitives-vs-objects
fuente
Para ser breve: los tipos primitivos son más rápidos y requieren menos memoria que los en caja
fuente