Es un poco difícil implementar una función de copia de objetos profundos. ¿Qué pasos toma para asegurarse de que el objeto original y el clonado no compartan ninguna referencia?
Advertencias: es posible que las clases anulen la serialización de modo que no se creen nuevas instancias , por ejemplo, para singletons. Además, por supuesto, esto no funciona si sus clases no son serializables.
Tenga en cuenta que la implementación FastByteArrayOutputStream proporcionada en el artículo podría ser más eficiente. Utiliza una expansión de estilo ArrayList cuando el búfer se llena, pero es mejor usar un enfoque de expansión de estilo LinkedList. En lugar de crear un nuevo búfer 2x y guardar el búfer actual, mantenga una lista vinculada de búferes, agregando uno nuevo cuando el actual se llene. Si recibe una solicitud para escribir más datos de los que cabrían en su tamaño de búfer predeterminado, cree un nodo de búfer que sea exactamente tan grande como la solicitud; los nodos no necesitan ser del mismo tamaño.
La lista vinculada de @BrianHarris no es más eficiente que la matriz dinámica. La inserción de elementos en una matriz dinámica es una complejidad constante amortizada, mientras que la inserción en una lista vinculada es una complejidad lineal
Norill Tempest
¿Cuánto serializar y deserializar más lentamente que el enfoque de constructor de copia?
Woland
75
Algunas personas han mencionado el uso o la anulación Object.clone(). No lo hagas Object.clone()tiene algunos problemas importantes y se desaconseja su uso en la mayoría de los casos. Consulte el Artículo 11, de " Java efectivo " de Joshua Bloch para obtener una respuesta completa. Creo que puedes usarlo con seguridadObject.clone() en matrices de tipo primitivo, pero aparte de eso, debe ser prudente sobre el uso y la anulación del clon.
Los esquemas que se basan en la serialización (XML o de otro tipo) son poco claros.
No hay una respuesta fácil aquí. Si desea copiar en profundidad un objeto, tendrá que atravesar el gráfico del objeto y copiar cada objeto secundario explícitamente a través del constructor de copia del objeto o un método de fábrica estático que a su vez copia en profundidad el objeto secundario. Los inmutables (p. Ej., StringS) no necesitan copiarse. Por otro lado, debe favorecer la inmutabilidad por este motivo.
Puede realizar una copia profunda con la serialización sin crear archivos.
Su objeto que desea copiar en profundidad deberá hacerlo implement serializable. Si la clase no es final o no se puede modificar, extienda la clase e implemente serializable.
En general, es una buena práctica escribir sus propios métodos de clonación para cada clase de un objeto en el gráfico de objeto que necesita clonación.
También está disponible enorg.apache.commons.lang.SerializationUtils
Pino
25
Una forma de implementar la copia profunda es agregar constructores de copia a cada clase asociada. Un constructor de copia toma una instancia de 'this' como argumento único y copia todos los valores de él. Bastante trabajo, pero bastante sencillo y seguro.
EDITAR: tenga en cuenta que no necesita utilizar métodos de acceso para leer los campos. Puede acceder a todos los campos directamente porque la instancia de origen siempre es del mismo tipo que la instancia con el constructor de copia. Obvio pero podría pasarse por alto.
Editar: tenga en cuenta que cuando usa constructores de copia necesita saber el tipo de tiempo de ejecución del objeto que está copiando. Con el enfoque anterior, no puede copiar fácilmente una lista mixta (es posible que pueda hacerlo con algún código de reflexión).
Solo me interesa el caso de que lo que está copiando es una subclase, pero el padre hace referencia a él. ¿Es posible anular el constructor de copia?
Pork 'n' Bunny
¿Por qué su clase principal se refiere a su subclase? ¿Puede dar un ejemplo?
Adriaan Koster
1
El automóvil de clase pública extiende el vehículo y luego se refiere al automóvil como un vehículo. originaList = new ArrayList <Vehicle>; copyList = new ArrayList <Vehicle>; originalList.add (auto nuevo ()); para (Vehículo vehículo: lista de vehículos) {copyList.add (nuevo Vehículo (vehículo)); }
Pork 'n' Bunny
@AdriaanKoster: si la lista original contiene un Toyota, su código colocará un Caren la lista de destino. La clonación adecuada generalmente requiere que la clase proporcione un método de fábrica virtual cuyo contrato establezca que devolverá un nuevo objeto de su propia clase; el propio constructor de copia debe protectedgarantizar que solo se utilizará para construir objetos cuyo tipo preciso coincida con el del objeto que se copia).
supercat
Entonces, si entiendo su sugerencia correctamente, ¿el método de fábrica llamaría al constructor de copia privada? ¿Cómo se aseguraría el constructor de copia de una subclase de que los campos de la superclase se inicialicen? ¿Puede dar un ejemplo?
Adriaan Koster
20
Puede usar una biblioteca que tenga una API simple y realice una clonación relativamente rápida con reflexión (debería ser más rápida que los métodos de serialización).
Cloner cloner =newCloner();MyClass clone = cloner.deepClone(o);// clone is a deep-clone of o
Nooo, no necesita la sobrecarga de xml-ing el objeto.
egelev
@egeleve Te das cuenta de que estás respondiendo a un comentario del '08 ¿verdad? Ya no uso Java y probablemente haya mejores herramientas ahora. Sin embargo, en ese momento, serializar en un formato diferente y luego volver a serializar parecía un buen truco, definitivamente era ineficiente.
sankara
10
Un enfoque muy fácil y simple es usar Jackson JSON para serializar objetos Java complejos a JSON y leerlo de nuevo.
Para usuarios de Spring Framework . Usando clase org.springframework.util.SerializationUtils:
@SuppressWarnings("unchecked")publicstatic<T extendsSerializable> T clone(T object){return(T)SerializationUtils.deserialize(SerializationUtils.serialize(object));}
Para objetos complicados y cuando el rendimiento no es significativo, uso una biblioteca json, como gson
para serializar el objeto en texto json, luego deserializar el texto para obtener un nuevo objeto.
gson que basado en la reflexión funcionará en la mayoría de los casos, excepto que los transientcampos no se copiarán y los objetos con referencia circular con causa StackOverflowError.
publicstatic<T> T copy(T anObject,Class<T> classInfo){Gson gson =newGsonBuilder().create();String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);return newObject;}publicstaticvoid main(String[] args){String originalObject ="hello";String copiedObject = copy(originalObject,String.class);}
Adhiérase a las convenciones de nomenclatura de Java para usted y nuestro bien.
Patrick Bergner
8
Use XStream ( http://x-stream.github.io/ ). Incluso puede controlar qué propiedades puede ignorar mediante anotaciones o especificando explícitamente el nombre de la propiedad a la clase XStream. Además, no necesita implementar una interfaz clonable.
La copia profunda solo se puede hacer con el consentimiento de cada clase. Si tiene control sobre la jerarquía de clases, puede implementar la interfaz clonable e implementar el método Clone. De lo contrario, es imposible hacer una copia profunda de forma segura porque el objeto también puede compartir recursos que no son de datos (por ejemplo, conexiones de bases de datos). Sin embargo, en general, la copia profunda se considera una mala práctica en el entorno Java y debe evitarse mediante las prácticas de diseño apropiadas.
Respuestas:
Una forma segura es serializar el objeto y luego deserializarlo. Esto asegura que todo sea una referencia nueva.
Aquí hay un artículo sobre cómo hacer esto de manera eficiente.
Advertencias: es posible que las clases anulen la serialización de modo que no se creen nuevas instancias , por ejemplo, para singletons. Además, por supuesto, esto no funciona si sus clases no son serializables.
fuente
Algunas personas han mencionado el uso o la anulación
Object.clone()
. No lo hagasObject.clone()
tiene algunos problemas importantes y se desaconseja su uso en la mayoría de los casos. Consulte el Artículo 11, de " Java efectivo " de Joshua Bloch para obtener una respuesta completa. Creo que puedes usarlo con seguridadObject.clone()
en matrices de tipo primitivo, pero aparte de eso, debe ser prudente sobre el uso y la anulación del clon.Los esquemas que se basan en la serialización (XML o de otro tipo) son poco claros.
No hay una respuesta fácil aquí. Si desea copiar en profundidad un objeto, tendrá que atravesar el gráfico del objeto y copiar cada objeto secundario explícitamente a través del constructor de copia del objeto o un método de fábrica estático que a su vez copia en profundidad el objeto secundario. Los inmutables (p. Ej.,
String
S) no necesitan copiarse. Por otro lado, debe favorecer la inmutabilidad por este motivo.fuente
Puede realizar una copia profunda con la serialización sin crear archivos.
Su objeto que desea copiar en profundidad deberá hacerlo
implement serializable
. Si la clase no es final o no se puede modificar, extienda la clase e implemente serializable.Convierta su clase en una secuencia de bytes:
Restaura tu clase desde una secuencia de bytes:
fuente
instance
en este caso?Puede hacer un clon profundo basado en serialización usando
org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang, pero tenga cuidado: el rendimiento es abismal.En general, es una buena práctica escribir sus propios métodos de clonación para cada clase de un objeto en el gráfico de objeto que necesita clonación.
fuente
org.apache.commons.lang.SerializationUtils
Una forma de implementar la copia profunda es agregar constructores de copia a cada clase asociada. Un constructor de copia toma una instancia de 'this' como argumento único y copia todos los valores de él. Bastante trabajo, pero bastante sencillo y seguro.
EDITAR: tenga en cuenta que no necesita utilizar métodos de acceso para leer los campos. Puede acceder a todos los campos directamente porque la instancia de origen siempre es del mismo tipo que la instancia con el constructor de copia. Obvio pero podría pasarse por alto.
Ejemplo:
Editar: tenga en cuenta que cuando usa constructores de copia necesita saber el tipo de tiempo de ejecución del objeto que está copiando. Con el enfoque anterior, no puede copiar fácilmente una lista mixta (es posible que pueda hacerlo con algún código de reflexión).
fuente
Toyota
, su código colocará unCar
en la lista de destino. La clonación adecuada generalmente requiere que la clase proporcione un método de fábrica virtual cuyo contrato establezca que devolverá un nuevo objeto de su propia clase; el propio constructor de copia debeprotected
garantizar que solo se utilizará para construir objetos cuyo tipo preciso coincida con el del objeto que se copia).Puede usar una biblioteca que tenga una API simple y realice una clonación relativamente rápida con reflexión (debería ser más rápida que los métodos de serialización).
fuente
Apache commons ofrece una forma rápida de clonar profundamente un objeto.
fuente
XStream es realmente útil en tales casos. Aquí hay un código simple para hacer clonación
fuente
Un enfoque muy fácil y simple es usar Jackson JSON para serializar objetos Java complejos a JSON y leerlo de nuevo.
http://wiki.fasterxml.com/JacksonInFiveMinutes
fuente
Para usuarios de Spring Framework . Usando clase
org.springframework.util.SerializationUtils
:fuente
Para objetos complicados y cuando el rendimiento no es significativo, uso una biblioteca json, como gson para serializar el objeto en texto json, luego deserializar el texto para obtener un nuevo objeto.
gson que basado en la reflexión funcionará en la mayoría de los casos, excepto que los
transient
campos no se copiarán y los objetos con referencia circular con causaStackOverflowError
.fuente
Use XStream ( http://x-stream.github.io/ ). Incluso puede controlar qué propiedades puede ignorar mediante anotaciones o especificando explícitamente el nombre de la propiedad a la clase XStream. Además, no necesita implementar una interfaz clonable.
fuente
La copia profunda solo se puede hacer con el consentimiento de cada clase. Si tiene control sobre la jerarquía de clases, puede implementar la interfaz clonable e implementar el método Clone. De lo contrario, es imposible hacer una copia profunda de forma segura porque el objeto también puede compartir recursos que no son de datos (por ejemplo, conexiones de bases de datos). Sin embargo, en general, la copia profunda se considera una mala práctica en el entorno Java y debe evitarse mediante las prácticas de diseño apropiadas.
fuente
fuente
Solía bulldozer para la clonación de objetos de Java y es genial en el que, Kryo biblioteca es otra gran alternativa.
fuente
BeanUtils hace un muy buen trabajo de clonación profunda de frijoles.
fuente
1)
Aquí su clase MyPerson y MyAddress deben implementar una interfaz serilazable
fuente
Usando Jackson para serializar y deserializar el objeto. Esta implementación no requiere que el objeto implemente la clase Serializable.
fuente