Miércoles 11 de Agosto

Records

Objetivos: Mostrar como se pueden usar las clases para agrupar datos en una sola entidad que puede ser manipulada como un solo dato compuesto.

Temas:


Ejercicio

Se tiene un archivo post.dat con los postulantes a las universidades chilenas. Cada línea de este archivo contiene el carnet de identidad (c.i.) y el nombre de un postulante en el siguiente formato:

    11478387-1:Fuenzalida Perez Alberto
    12245894-7:Fernandez Henriquez Pedro
    ...
El archivo se encuentra ordenado por carnet de identidad. El problema consiste en ordenar el archivo por apellidos y nombres de los postulantes, siguiendo el orden lexicográfico.

Primera solución:

Colocar toda la información en dos arreglos de strings. El primero (cis) contiene los carnets de identidad y el segundo (nombres) los nombres. De esta forma nombres[i] es el nombre del postulante con carnet de identidad cis[i]. Es crucial preservar esta relación al realizar el ordenamiento.

Luego, se ordena el arreglo de nombres mediante cualquiera de los algoritmos vistos anteriormente. Para preservar la asociación entre valores en el arreglo nombres y valores en el arreglo cis, cada vez que se hace un intercambio en el arreglo nombres, el mismo intercambio se reproduce en el arreglo cis.

Observe que en este problema, el hecho de que el archivo esté ordenado por carnet de identidad no significa nada. Es lo mismo que el arreglo esté desordenado.

Desventaja:

Supongamos que en el archivo se encuentra también el puntaje en la PAA. Si se desea ordenar el archivo, hay que crear 3 arreglos y al ordenar, hay que intercambiar simultáneamente en los 3 arreglos. Si tenemos n campos de información en el archivo, hay que crear n arreglos e intercambiar en todos los arreglos. Desde luego, esto sería demasiado incómodo. En esta clase veremos una mejor solución.


Uso de objetos como records

Un record es la agrupación de un conjunto de datos en una sola estructura que puede ser manipulada como un solo dato o también a través de sus partes. En Java, estas agrupaciones se realizan mediante la definición de clases sin métodos. Por ejemplo, para almacenar la información relativa a un postulante se puede definir la siguiente clase:

    class Post {
      String ci;
      String nombre;
      int ptje;
    }
Los objetos de esta clase son records que agrupan la información de un postulante. Para crear un record, procedemos simplemente a crear un objeto de la clase Post e inicializar todos sus variables de instancia:

    Post post= new Post();
    post.ci= "11478387-1";
    post.nombre= "Fuenzalida Perez Alberto";
    post.ptje= 715;
La información contenida en el objeto se puede escribir en un archivo mediante:

    escr.println(post.ci+":"+post.nombre+":"+post.ptje);
en donde escr es un objeto de la clase TextWriter para escribir en un archivo.


Arreglos de objetos

Así como en Java es pueden crear arreglos de enteros, reales y strings, también es posible crear arreglos de objetos. Por ejemplo:

    Post[] posts= new Post[100];
Crea un arreglo de 100 referencias a objetos de la clase Post. Observe que los paréntesis son [ ]. Si se usara (100), se estaría creando una instancia de Post, no un arreglo.

Por otra parte, así como post referencia un objeto, posts[i] también referencia un objeto. Por lo tanto, si se desea usar este arreglo, es necesario crear 100 objetos. Por ejemplo:

    int i=0;
    while (i<100) {
      posts[i]= new Post();
      i= i+1;
    }
En este caso, los paréntesis son (), pues se desea crea un objeto.

El siguiente programa lee el archivo post.dat y construye un arreglo con la información:

    int npost= ...; // el numero de postulantes en el archivo
    Post[] posts= new Post[npost];
    TextReader lect= new TextReader("post.dat");
    int i= 0;
    while (true) {
      String lin= lect.readLine();
      if (lect.eofReached())
        break;
      FieldParser decod= new FieldParser(lin, ":");

      Post post= new Post();
      post.ci= decod.readString();
      post.nombre= decod.readString();
      post.ptje= decod.readInt();
      posts[i]= post;

      i= i+1;
    }
Las 5 líneas intermedias se pueden escribir también de la siguiente forma:

    posts[i]= new Post();
    posts[i].ci= decod.readString();
    posts[i].nombre= decod.readString();
    posts[i].ptje= decod.readInt();
La variable posts es un arreglo de objetos de la clase Post. Dado que la clase en sí no posee métodos ni constructores, en este caso diremos que la clase es simplemente un record. Su único objetivo es agrupar información para manejarla como un todo.

Por ejemplo, si se desea intercambiar el postulante que se encuentran en la i-ésima posición con el que se encuentra en la j-ésima posición basta ejecutar:

    Post aux= posts[i];
    posts[i]= posts[j];
    posts[j]= aux;
Es decir se realiza un solo intercambio. Esto se puede hacer porque posts[i] es un objeto compuesto por de 3 ítemes: c.i., nombre y puntaje. Pero este objeto se puede manipular como un solo dato.

También se podría haber hecho:

    String aux= posts[i].ci;
    posts[i].ci= posts[j].ci;
    posts[j].ci= aux;
    aux= posts[i].nombre;
    posts[i].nombre= posts[j].nombre;
    posts[j].nombre= aux;
    int aux2= posts[i].ptje;
    posts[i].ptje= posts[j].ptje;
    posts[j].ptje= aux;
Pero esto es claramente más trabajo que lo anterior.


Ordenamiento de arreglos de objetos

Ahora, procedemos a ordenar el arreglo de postulantes por nombre. Para realizar este ordenamiento, no es necesario pensar un nuevo algoritmo. Sólo se modifica ligeramente cualquiera de los algoritmos anteriores. Por ejemplo, podemos partir del algoritmo de la burbuja:

    void burbuja(int[ ] a, int n) {
      int iult= n-1;
      boolean cambio= true;
      while (cambio) {
        int i= 0;
        cambio= false;
        while (i<iult) {
          if (a[i]>a[i+1]) {
            // intercambiar
            int aux= a[i];
            a[i]= a[i+1];
            a[i+1]= aux;
            cambio= true;
          }
          i= i+1;
        }
        iult= iult-1;
      }
    }
Lo único que hay que cambiar son las partes que están remarcadas. Es decir el tipo del arreglo que se debe ordenar, la comparación entre los elementos del arreglo y el tipo de la variable auxiliar que se usa para intercambiar dos elementos:

  
    void ordenaPosts(Post[ ] a, int n) {
      int iult= n-1;
      boolean cambio= true;
      while (cambio) {
        int i= 0;
        cambio= false;
        while (i<iult) {
          if (compare(a[i].nombre,a[i+1].nombre)>0) {
            // intercambiar
            Post aux= a[i];
            a[i]= a[i+1];
            a[i+1]= aux;
            cambio= true;
          } 
          i= i+1;
        }   
        iult= iult-1;
      }   
    }