¿Java tiene tipos mutables para Integer, Float, Double, Long?
81
Estoy en una situación en la que quiero usar versiones mutables de cosas como Integer. ¿Tengo que usar estas clases (a continuación) o Java tiene algo integrado?
En algunos casos (p. Ej., Un juego, almacenar un trozo de comida que contenga ncalorías, que se pueden agotar / agregar), podría ser mejor usar una clase con el nombre del uso (p. Ej. class FoodItem { int calories; }, Porque es más claro y los métodos pueden ser agregado si es necesario más tarde.
GKFX
7
Las lambdas de Java 8 funcionan solo con variables finales de manera efectiva. Para evitar esta limitación, se necesita un int mutable.
Alex
Es posible que desee un contador que deba pasarse entre métodos. Pasar un intno funciona como si se incrementara en un método, entonces el valor no se reflejará en el otro método.
Adam Burley
Respuestas:
51
No, Java no los tiene integrados. Y eso es por una razón. Usar tipos mutables es peligroso, ya que se pueden usar incorrectamente fácilmente. Además, es muy fácil de implementar. Por ejemplo, commons-lang tiene la extensión MutableInt.
Supongo que el desarrollador de Java quería que Integer 'se comporte' como un int, que es ... una vez que tiene una referencia a él, nunca cambia (evite la confusión). But..it todavía parece extraño no tener una opción mutable de alguna manera, a mí ...
rogerdpack
35
"El uso de tipos mutables es peligroso porque se pueden utilizar incorrectamente". La mayoría de las cosas se pueden usar incorrectamente. Existen tipos inmutables para los casos en los que la seguridad es importante, aunque pueden modificarse por reflexión. Con esos casos fuera del camino, no hay razón para evitar que las personas usen tipos mutables cuando los necesiten.
GKFX
2
Para decirlo mejor, los tipos inmutables crean menos confusión, por ejemplo, en mapas y conjuntos. Si tuviera un Entero mutable y cambiara su valor donde servía como clave, estropearía la colección. Sin embargo, en caso de que necesite alguna implementación mutable de Integer, nada es más fácil que crear una clase con un valor int dentro. Y puede ser creativo allí, hágalo, por ejemplo, un contador o una cuenta atrás, no solo un int simple que permita casi cualquier cosa. Déle algo de lógica (a menos que desarrolle en Java EE que funcione de abajo hacia arriba).
Vlasec
Ciertamente es una vieja pregunta, pero es de esperar que alguien pueda responder CÓMO se pueden usar mal. ¿Qué tiene de peligroso usarlos?
The Fluffy Robot
@ user1175807 Como había comentado Vlasec, si modifica accidentalmente una variable que ha utilizado en otro lugar, provocará errores extraños. Peor aún, digamos que está escribiendo un programa que funciona junto con el código de un cliente. Necesita implementar un método que tenga un parámetro de tipo Integer mutable. Ahora bien, si modifica el Integer mutable por cualquier motivo, también se cambiará en el programa del cliente, lo cual es totalmente inesperado y provocará errores que son difíciles de encontrar.
GregT
90
Siempre puede envolver el valor en una matriz como int[] mutable = {1};si incluir el código para una clase contenedora mutable fuera demasiado engorroso.
Esta seguridad de la cabeza tiene un costo de rendimiento, que en muchos casos no es necesario. Por ejemplo, un caso de uso común para mí es incrementar un contador capturado por una lambda. Usar un atómico es excesivo.
Minas Mina
12
Aquí hay una pequeña clase que hice para un entero mutable:
En mi humilde opinión, la mejor opción es que no da pistas falsas sobre la concurrencia como lo hace el uso de un número entero atómico. Y una matriz, realmente nunca haría algo tan malo en mi código ..;)
Snicolas
Podría ser una buena idea para proporcionar una única clase de contenedor genérico en lugar de modo que no terminar con una MutableInteger, MutableDouble, MutableStringetc, pero en cambio tienen una Mutable<Integer>, Mutable<Double>, ... La sobrecarga de la memoria (de utilizar Integermás de intlo general no caer en la cuenta. Pero obtienes una clase única, lista para usar que puede manejar la mayoría de los casos (aunque si quieres que tu número entero sea comparable o cosas similares, aún necesitas subclase).
intes siempre mutable a menos que también lo seafinal
Peter Lawrey
18
No es realmente válido decir que un tipo primitivo es mutable . Se puede cambiar, pero también se pueden cambiar todos los objetos no finales si solo apunta a un nuevo objeto. Por ejemplo, Integer a = 4;entonces a = 5;es un código válido, pero Integerno es mutable.
mjaggard
Estoy de acuerdo en que las Integerinstancias no son mutables incluso si las referencias a ellas lo son, pero ese es un tipo diferente.
Peter Lawrey
1
intno es mutable porque si lo pasa a un método, no hay forma de que el método cambie su valor y refleje el nuevo valor en el método de llamada
Adam Burley
5
AtomicIntegerya se ha mencionado. DoubleSe puede emular un s mutable AtomicReference<Double>. Se aplican las advertencias ya mencionadas y es de mal estilo, pero a veces tienes un código como este
y desea refactorizarlo en estilo funcional Java 8. Si el código sigue este patrón pero le agrega una complejidad considerable, la conversión más sensata podría ser
Por supuesto, este es al menos un estilo cuestionable. Pero me encontré más de una vez en una situación con un bucle retorcido sobre una ResultSetcomputadora y en parte acumulando tres datos diferentes. Esto hace que sea realmente difícil convertir el código en un estilo funcional adecuado. Convertir las partes acumuladas de acuerdo con el patrón anterior me pareció una compensación razonable entre un código limpio y una refactorización demasiado simplificada.
Realmente debería pensar en su redacción: incluso si usa una lambda, su ejemplo no es funcional , ya que prohíbe los efectos secundarios. Si realmente lo escribe funcional, no es necesario mutables: someStreamGenerator().mapToDouble(Data::getValue).sum(). Incluso para acumular tres informaciones diferentes, existe una forma funcional mediante el uso de Stream.reduceo Stream.collect. No veo ninguna razón para refactorizar cada bucle en un fragmento de código funcional, pero si quiere ir por ese camino, debe hacerlo hasta el final.
Tobias Liefke
Como escribí: Este no es el estilo adecuado y para todos y cada uno de los códigos nuevos no debe usarse. Pero al refactorizar más de 400.000 líneas de código que evolucionaron en más de 15 años, encontrará construcciones en las que la reescritura adecuada en construcciones verdaderamente funcionales puede ser extremadamente difícil. Entonces, reescribir en ese estilo híbrido puede ser una compensación sensata, ya que logra alinear al menos la estructura básica. fory foreachpueden ser partes de marcos complejos que se reescriben funcionalmente, por lo que debe alinear el resto del código de alguna manera alrededor de ellos.
Dirk Hillbrecht
Estás confundiendo lambdas y programación funcional. En su ejemplo, no lo reescribió como "funcional". ¿Y de qué sirve reescribir todo con lambdas, cuando se pierde legibilidad, pero no se obtienen las ventajas de la programación funcional? ¿Por qué debería uno intentar deshacerse de todos los bucles en un marco complejo utilizando tales híbridos?
Tobias Liefke
Tienes 1000 lugares con un cierto idioma en tu código. El idioma es impulsado por una biblioteca o cualquier estructura global. Reemplaza esa estructura global con una nueva basada en un enfoque funcional y lambdas. 998 de sus 1000 lugares se pueden convertir limpiamente en un estilo funcional. Los 2 restantes son extremadamente difíciles de convertir correctamente. En tales casos, siempre ordeno convertirme a un nuevo estilo con tales construcciones híbridas. Solo entonces podrá deshacerse de las viejas estructuras globales que haya en el código. Si bien no es perfecto, la calidad general del código aumenta.
Dirk Hillbrecht
2
Puede importar el paquete org.omg.CORBA (o solo la clase que necesita) y en él puede usar las clases de Holder.
Por ejemplo, tiene el "IntHolder" donde el campo donde almacena el entero es público, dando acceso para modificarlo.
Si bien es una forma muy inteligente de solucionar el problema, con Java9 en camino, probablemente no desee usar esto, eso importaría todo el módulo CORBA solo para un titular int.
n
calorías, que se pueden agotar / agregar), podría ser mejor usar una clase con el nombre del uso (p. Ej.class FoodItem { int calories; }
, Porque es más claro y los métodos pueden ser agregado si es necesario más tarde.int
no funciona como si se incrementara en un método, entonces el valor no se reflejará en el otro método.Respuestas:
No, Java no los tiene integrados. Y eso es por una razón. Usar tipos mutables es peligroso, ya que se pueden usar incorrectamente fácilmente. Además, es muy fácil de implementar. Por ejemplo, commons-lang tiene la extensión
MutableInt
.fuente
Siempre puede envolver el valor en una matriz como
int[] mutable = {1};
si incluir el código para una clase contenedora mutable fuera demasiado engorroso.fuente
Desde JDK 1.5 java ahora tiene
java.util.concurrent.atomic.AtomicInteger
Este es un entero mutable seguro para subprocesos, ejemplo de uso:
final AtomicInteger value = new AtomicInteger(0);
luego más tarde:
fuente
Aquí hay una pequeña clase que hice para un entero mutable:
public class MutableInteger { private int value; public MutableInteger(int value) { this.value = value; } public void set(int value) { this.value = value; } public int intValue() { return value; } }
Fácilmente podría extender esto a cualquier otro primitivo. Por supuesto, como dicen todos los demás, debes usarlo con cuidado.
fuente
MutableInteger
,MutableDouble
,MutableString
etc, pero en cambio tienen unaMutable<Integer>
,Mutable<Double>
, ... La sobrecarga de la memoria (de utilizarInteger
más deint
lo general no caer en la cuenta. Pero obtienes una clase única, lista para usar que puede manejar la mayoría de los casos (aunque si quieres que tu número entero sea comparable o cosas similares, aún necesitas subclase).Puede usar un nnnn [] como un objeto mutable para cualquier tipo primitivo como sugiere @Alexandre, java también tiene AtomicInteger y AtomicLong.
En mi humilde opinión, int suele ser una mejor opción que Integer y eso es mutable.
¿Puede obtener más detalles de por qué necesita un objeto múltiple? Quizás haya otra forma de lograr lo mismo.
fuente
int
es siempre mutable a menos que también lo seafinal
Integer a = 4;
entoncesa = 5;
es un código válido, peroInteger
no es mutable.Integer
instancias no son mutables incluso si las referencias a ellas lo son, pero ese es un tipo diferente.int
no es mutable porque si lo pasa a un método, no hay forma de que el método cambie su valor y refleje el nuevo valor en el método de llamadaAtomicInteger
ya se ha mencionado.Double
Se puede emular un s mutableAtomicReference<Double>
. Se aplican las advertencias ya mencionadas y es de mal estilo, pero a veces tienes un código como estedouble sum=0 for (Data data:someListGenerator()) sum+=data.getValue()
y desea refactorizarlo en estilo funcional Java 8. Si el código sigue este patrón pero le agrega una complejidad considerable, la conversión más sensata podría ser
AtomicReference<Double> sumref=new AtomicReference<>(0d); someStreamGenerator().forEach(data-> sumref.set(sumref.get().doubleValue()+data.getValue())); double sum=sumref.get().doubleValue();
Por supuesto, este es al menos un estilo cuestionable. Pero me encontré más de una vez en una situación con un bucle retorcido sobre una
ResultSet
computadora y en parte acumulando tres datos diferentes. Esto hace que sea realmente difícil convertir el código en un estilo funcional adecuado. Convertir las partes acumuladas de acuerdo con el patrón anterior me pareció una compensación razonable entre un código limpio y una refactorización demasiado simplificada.fuente
someStreamGenerator().mapToDouble(Data::getValue).sum()
. Incluso para acumular tres informaciones diferentes, existe una forma funcional mediante el uso deStream.reduce
oStream.collect
. No veo ninguna razón para refactorizar cada bucle en un fragmento de código funcional, pero si quiere ir por ese camino, debe hacerlo hasta el final.for
yforeach
pueden ser partes de marcos complejos que se reescriben funcionalmente, por lo que debe alinear el resto del código de alguna manera alrededor de ellos.Puede importar el paquete org.omg.CORBA (o solo la clase que necesita) y en él puede usar las clases de Holder.
Por ejemplo, tiene el "IntHolder" donde el campo donde almacena el entero es público, dando acceso para modificarlo.
public static void triple(IntHolder x){ x.value = 3 * x.value; } IntHolder mutableInt = new IntHolder(10); triple(mutableInt); System.out.println(mutableInt.value);
También tiene "LongHolder" y "DoubleHolder" y muchos otros que puedes usar. Úselo con precaución.
Aquí está la API para ello: https://docs.oracle.com/javase/7/docs/api/org/omg/CORBA/package-summary.html
fuente