Duplicación de objetos (arrays)

prev.gif (997 bytes)chapter.gif (1105 bytes)home.gif (1054 bytes)next.gif (998 bytes)

Pasando datos a una función

Duplicación de arrays unidimensionales

Duplicación de arrays bidimensionales


Duplicación de arrays unidimensionales

disco.gif (1035 bytes)clonico1: Lista.java, ClonicoApp1.java

Veamos de nuevo la clase Lista que hemos cambiado ligeramente para mostrar algunas particularidades del método clone. Le hemos añadido un string que guarda el nombre de la lista.

public class Lista implements Cloneable{
    private int[] x;
    public int n;
    public String nombre;
//...
}

Para duplicar un objeto de la clase Lista hemos de implementar el interface Cloneable y redefinir la función miembro clone de la clase base Object, de forma similar a como se hizo para definir la versión clone de la clase Rectangulo. Recuérdese que los arrays vienen descritos por clases en Java. Por tanto, la versión clone de la clase Lista ha de contener el código correspondiente a la copia del subobjeto array unidimensional de enteros int[].

    public Object clone(){
        Lista obj=null;
        try{
            obj=(Lista)super.clone();
        }catch(CloneNotSupportedException ex){
            System.out.println(" no se puede duplicar");
        }
        obj.x=(int[])obj.x.clone();
        return obj;
    }

Como podemos apreciar en el código, el subobjeto de la clase String se copia de forma automática como los tipos básicos de datos, sin necesidad de hacerlo de forma explícita.

El código completo de este ejemplo, es el siguiente

public class Lista implements Cloneable{
    private int[] x;
    public int n;
    public String nombre;
    public Lista(int[] x, String nombre) {
        this.x=x;
        n=x.length;
        this.nombre=nombre;
    }
    public Object clone(){
        Lista obj=null;
        try{
            obj=(Lista)super.clone();
        }catch(CloneNotSupportedException ex){
            System.out.println(" no se puede duplicar");
        }
        obj.x=(int[])obj.x.clone();
        return obj;
    }
    private void ordenar(){
        int aux;
        for(int i=0; i<n-1; i++){
            for(int j=i+1; j<n; j++){
                if(x[i]>x[j]){
                    aux=x[j];
                    x[j]=x[i];
                    x[i]=aux;
                }
            }
        }
    }
    public int menor(){
        Lista aux=(Lista)clone();
        aux.ordenar();
        return aux.x[0];
    }

    public int mayor(){
        ordenar();
        return x[n-1];
    }
   public String toString(){
        String texto=nombre;
        for(int i=0; i<n; i++){
            texto+="\t"+x[i];
        }
        return texto;
   }
}

 

Modificación del objeto cuando se llama a las funciones miembro

Para hallar el mayor de una lista de números, ordenamos los datos en orden creciente y luego, retornamos el último.

    public int mayor(){
        ordenar();
        return x[n-1];
    }

La función pública mayor llama a ordenar, la cual modifica el orden de los elementos del array miembro dato x. El objeto resulta modificado al llamar a la función miembro mayor.

Si por algún motivo deseamos preservar el orden original de los datos, procedemos del modo que se indica en la definición de la función menor.

    public int menor(){
        Lista aux=(Lista)clone();
        aux.ordenar();
        return aux.x[0];
    }

Creamos una copia aux de this (línea marcada en negrita), modificamos la copia aux llamando a la función miembro ordenar, pero dejando intacto a this, y por último, devolvemos el primer elemento del array x de aux. El objeto aux de la clase Lista es temporal, ya que solamente existe en el cuerpo de la función menor.

Si hemos redefinido la función miembro toString en la clase Lista, podemos comprobar como la llamada a la función menor no modifica al objeto que la llama, y la llamada a la función mayor modifica al objeto que la llama.

        int[] datos={2, 5, -1, 3, 0};
        Lista lista=new Lista(datos, "ordenar");
        System.out.println("antes ----->"+ lista);
        lista.menor();
        System.out.println("despues ---->"+ lista);
        System.out.println("************************");
        System.out.println("antes ------> "+ lista);
        lista.mayor();
        System.out.println("despues ----> "+ lista);

 

Duplicación de arrays bididimensionales

disco.gif (1035 bytes)clonico2: Matriz.java, ClonicoApp2.java

Finalizaremos este estudio, con la duplicación de un objeto, uno de cuyos miembros es un array bidimensional.

Veamos la definición de la clase Matriz cuadrada, que tiene como miembros dato un array bidimensional y la dimensión de la matriz.

public class Matriz implements Cloneable{
    private int[][] x;
    public int n;
//...
}

De nuevo, para poder hacer copias de los objetos de la clase Matriz hemos de implementar el interface Cloneable, y redefinir la función miembro clone de la clase base Object.

Dado que los objetos de la clase Matriz son compuestos, contienen subobjetos de la clase int[][], la redefinición de clone será similar a la de la clase Rectangulo y Lista, pero además, hemos de entender que es un array bidimensional.

Un array bidimensional está formado por arrays unidimensionales. Luego, para copiar un array bidimensional hemos de copiar cada uno de los arrays unidimensionales que lo forman.

    public Object clone(){
        Matriz obj=null;
        try{
            obj=(Matriz)super.clone();
        }catch(CloneNotSupportedException ex){
            System.out.println(" no se puede duplicar");
        }
        obj.x=(int[][])obj.x.clone();
        for(int i=0; i<obj.x.length; i++){
            obj.x[i]=(int[])obj.x[i].clone();
        }
        return obj;
    }
public class Matriz implements Cloneable{
    private int[][] x;
    public int n;
    public Matriz(int[][] x) {
        this.x=x;
        n=x.length;
    }
    public Object clone(){
        Matriz obj=null;
        try{
            obj=(Matriz)super.clone();
        }catch(CloneNotSupportedException ex){
            System.out.println(" no se puede duplicar");
        }
        obj.x=(int[][])obj.x.clone();
        for(int i=0; i<obj.x.length; i++){
            obj.x[i]=(int[])obj.x[i].clone();
        }
        return obj;
    }
    public void modificar(){
       for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                x[i][j]=x[j][i]+i-j;
            }
       }
    }
   public String toString(){
        String texto="";
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
            texto+="\t"+x[i][j];
            }
            texto+="\n";
        }
        texto+="\n";
        return texto;
   }
}

Hemos definido en la clase Matriz una función miembro denominada modificar que modifica los valores que guarda el array bidimensional del objeto que la llama. Esta función puede realizar cualquier tarea como la de transformar una matriz en otra triangular para calcular su determinante.

Supongamos una función f y otra g a la que se le pasa un objeto de la clase Matriz en su único argumento. En el cuerpo de f y g se realizan diversas tareas con el objeto que se ha pasado por ejemplo, llamar a la función miembro modificar.

    void f(Matriz matriz){
        matriz=(Matriz)matriz.clone();
        matriz.modificar();
    }
    void g(Matriz matriz){
        matriz.modificar();
    }

En la primera versión f, la llamada a la función miembro modificar la realiza una copia de la matriz que se le pasa, mientras que en la función g, la llamada a la función miembro modificar se realiza a partir de la matriz que se le pasa.

Si la clase Matriz ha redefinido la función miembro toString de la clase base Object, podemos comprobar el efecto de las llamadas a las funciones f y g.

        int[][] datos={{2, 5, -1}, {0, -2, 4}, {1, -3, 2}};
        Matriz obj=new Matriz(datos);
        System.out.println("antes "+ obj);
        f(obj);
        System.out.println("después "+ obj);
        System.out.println("************************");
        System.out.println("antes "+ obj);
        g(obj);
        System.out.println("después "+ obj);

Comprobaremos que la llamada de f preserva la matriz obj original, mientras que la llamada de g, modifica el objeto original obj.