En PC Resumen hablaremos de la clase Array en Java. Esta clase proporciona métodos estáticos para manipular tablas. Proporciona métodos como sort para ordenar, binarySearch para buscar en una tabla ordenada, equals para compararlas y fill para poner valores. Además los métodos sort y binarySearch están sobrecargados con versiones genéricas que permiten a los programadores ordenar y buscar en tablas que contengan objetos de cualquier tipo. En el programa siguiente podemos ver la utilización de estos métodos.

public class UsoArrays {
    private int arrayInt[] = {1, 2, 3, 4, 5, 6};
    private double arrayDouble[] = {8.4, 9.3, 0.2, 7.9, 3.4};
    private int arrayIntPle[], copiaArrayInt[];
    
    public UsArrays() {
        arrayIntLleno = new int[10];
        copiaArrayInt = new int[arrayInt.length];
        Arrays.fill(arrayIntPle, 7);
        Arrays.sort(arrayDouble);
        System.arraycopy(arrayInt, 0, copiaArrayInt, 0, arrayInt.length);
    }

    public void imprimirArrays() {
        System.out.print("arrayDouble: ");
        for (double valorDouble : arrayDouble) {
            System.out.printf("%.1f ", valorDouble);
        }
        System.out.print("\narrayInt: ");
        for (int valorInt : arrayInt) {
            System.out.printf("%d ", valorInt);
        }
        System.out.print("\narrayIntLleno: ");
        for (int valorInt : arrayIntLleno) {
            System.out.printf("%d ", valorInt);
        }
        System.out.print("\ncopiaArrayInt: ");
        for (int valorInt : copiaArrayInt) {
            System.out.printf("%d ", valorInt);
        }
        System.out.println("\n");
    }

    public int buscarUnInt(int valor) {
        return Arrays.binarySearch(arrayInt, valor);
    }

    public void imprimirIgualdad() {
        boolean b = Arrays.equals(arrayInt, copiaArrayInt);
        System.out.printf("arrayInt %s copiaArrayInt\n", (b ? "==" : "!="));
        b = Arrays.equals(arrayInt, arrayIntPle);
        System.out.printf("arrayInt %s arrayIntPle\n", (b ? "==" : "!="));
    }

    public static void main(String args[]) {
        UsoArrays usoArrays = new UsoArrays();
        usoArrays.imprimirArrays();
        usoArrays.imprimirIgualdad();
        int ubicacion = usoArrays.buscarUnInt(5);
        if (ubicacion >= 0) {
            System.out.printf("Se ha encontrado el 5 en el elemento %d de arrayInt\n", ubicacion);
        } else {
            System.out.println("No se ha encontrado el 5 en arrayInt");
        }
        ubicacion = usoArrays.buscarUnInt(8763);
        if (ubicacio >= 0) {
            System.out.printf("Se ha encontrado el 8763 en el element %d de arrayInt\n", ubicacio);
        } else {
            System.out.println("No se ha encontrado el 8763 en arrayInt");
        }
    }
}

El método binarySearch funciona de la siguiente manera: si se encuentra el valor devuelve el índice del elemento, de lo contrario devuelve un valor negativo. El valor negativo se basa en el punto de inserción de la clave de búsqueda: el índice donde se debería insertar la clave si se tuviera que hacer una operación de inserción. Una vez el método determina el punto de inserción, cambia el signo de éste a negativo y le resta uno. Por ejemplo en el código anterior el punto de inserción para el valor 8763 se el índice 6, por lo tanto el método devuelve -7. Este valor de retorno es útil para agregar elementos en una tabla ordenada.

  • Si pasamos una mesa desordenada el valor devuelto es indefinido.
  • Si hay valores repetidos, el método no garantiza, cuál de ellos encuentra.

En PC Resumen hablaremos de las colecciones de Java. Una colección es una estructura de datos que puede guardar referencias a otros objetos. Las interfaces del marco de trabajo de colecciones declaran las operaciones que deben realizar de forma genérica en diferentes tipos de colecciones. En la tabla siguiente se pueden ver algunas de estas interfaces. Hay diferentes implementaciones de estas interfaces en el marco de trabajo.

Interfaz Descripción
Collection La interfaz raíz de la jerarquía, a partir de la cual se derivan Set, Queue y List
Set Una colección que no contiene elementos repetidos (un conjunto)
List Una colección ordenada que puede tener elementos repetidos
Map Asocia claves con valores y no puede contener claves duplicadas
Queue Es por regla general una estructura de tipo cola, pero se puede modificar

 

Desde Java 5, las colecciones son genéricas y por lo tanto podemos especificar el tipo exacto que almacena ahorrándonos errores.

En PC Resumen, hablaremos del concepto de como funcionan los Comodines que tienen los genéricos en Java.

Antes de ver los comodines, considérese un ejemplo que nos ayude a motivar su uso. Supongamos que queremos implementar un método genérico llamado sumar que obtenga el total de números de una colección, como en un objeto ArrayList. Nos gustaría poder obtener el total de todos los números sin importar su tipo. Por esta razón, declaramos el objeto con el argumento de tipo Number, el cual es superclase de todos los números. Por lo tanto el método sumar recibirá un parámetro de tipo ArrayList<Number> y obtendrá el total de sus elementos.

import java.util.ArrayList;

public class TotalNumeros {
    
    public static double sumar (ArrayList<Number> lista) {
        double total = 0;
        for (Number elemento: lista) {
            total += element.doubleValue();
        }
        return total;
    }

    public static void main(String args[]) {
        Number[] numeros = {1, 2.4, 3, 4.1};
        ArrayList<Number> listaNumeros = new ArrayList<Number>();
        for (Number elemento: numeros) {
            listaNumeros.add (elemento);
        }
        System.out.printf("listaNumeros contiene:%s \n", listaNumeros);
        System.out.printf("Total de los elementos a listaNumeros: %.1f \n", sumar(llistaNumeros));
    }
}

El método utiliza valores double para realizar los cálculos y devuelve el resultado como un double.

En PC Resumen vamos a hablar de los genéricos crudos en Java. Los programas de prueba anteriores que hemos visto en el artículo Clases genéricas en Java, crean instancias de objetos Pila con los argumentos de tipo Double y Integer. También es posible instanciar la clase genérica Pila sin especificar un argumento de tipo:

Pila pila = new Pila(5);

En este caso, se dice que pila tiene un tipo crudo, lo que significa que el compilador utiliza de forma implícita el tipo Object en la clase genérica para cada argumento de tipo. Así, la instrucción anterior crea una Pila que puede almacenar objetos de cualquier tipo. Esto es importante para la compatibilidad con versiones anteriores de Java.

A una variable Pila de crudo se le puede asignar una Pila que especifique un argumento de tipo:

Pila pilaTipoCrudo2 = new Pila<Double>(5);

Debido a que el tipo Double es una subclase de Object. La asignación se permite ya que los elementos son objetos.

De forma similar, a una variable Pila que especifica un argumento de tipo se le puede asignar un objeto Pila de crudo:

Pila<Integer> pilaInteger = new Pila(10);

Aunque esta asignación se permite no es segura debido a que una Pila de crudo podría almacenar elementos diferentes a Integer. En este caso, el compilador genera un mensaje de advertencia, el cual indica asignación insegura.

El programa de prueba siguiente utiliza la noción de tipo crudo. Si compilamos con la opción Xlint: Unchecked saldrán varios mensajes de advertencia ya que el compilador no puede garantizar que los tipos manipulados por las pilas sean correctos.

public class PruebaTipoCrudo {
    private Double[] elementosDouble = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
    private Integer[] elementosInteger = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
    
    public void probarPilas() {
        Pila pilaTipoCrudo1 = new Pila(5);
        Pila pilaTipoCrudo2 = new Pila<Double>(5);
        Pila<Integer> pilaInteger = new Pila(10);
        probarApilar("pilaTipoCrudo1", pilaTipoCrudo1, elementsDouble);
        probarDesapilar("pilaTipoCrudo1", pilaTipoCrudo1 );
        probarApilar("pilaTipoCrudo2", pilaTipoCrudo2, elementsDouble);
        probarDesapilar("pilaTipoCrudo2", pilaTipoCrudo2);
        probarApilar("pilaInteger", pilaInteger, elementsInteger);
        probarDesapilar("pilaInteger", pilaInteger);
    }

    public <T> void probarApilar(String nombre, Pila<T> pila, T[] elementos) {
        try {
            System.out.printf("\n Apilar elementos en %s \n", nombre);
            for (T elemento: elementos) {
                System.out.printf("%s", elemento);
                pila.apilar(elemento);
            }
        } Catch (ExcepcioPilaLlena excepcioPilaLlena) {
            System.out.println();
            excepcioPilaLlena.printStackTrace();
        }
    }

    public <T> void probarDesapilar(String nombre, Pila<T> pila) {
        try {
            System.out.printf("\n Desapilando elementos de %s \n", nombre);
            T valorAQuitar;
            while (true) {
                valorAQuitar = pila.desapilar ();
                System.out.printf("%s", valorAQuitar);
            }
        } Catch (ExcepcioPilaVacia excepcioPilaVacia) {
            System.out.println();
            excepcioPilaVacia.printStackTrace();
        }
    }

    public static void main(String args[]) {
        PruebaTipoCrudo aplicacion = new PruebaTipoCrudo();
        aplicacion.probarPiles ();
    }
}

En PC Resumen hablaremos de que son las Clases genéricas en Java. El concepto de una estructura de datos, como por ejemplo una pila, se puede entender independientemente del tipo de elemento que manipula. Las clases genéricas proporcionan los medios para describir el concepto de pila (o cualquier otra clase) en forma independiente de su tipo. Así podemos crear instancias de objetos con tipos específicos de la clase genérica. Esto nos permite una gran oportunidad de reutilizar el software.

Una vez que tenemos una clase genérica, podemos utilizar una notación concisa para indicar el tipo actual a utilizarse en lugar del parámetro de tipo de la clase. En tiempo de compilación, el compilador Java asegura la seguridad de los tipos de nuestro código y utiliza las técnicas de borrado para permitir que nuestro código interactúe con la clase genérica.

En el código siguiente vemos la declaración de una clase genérica Pila. La declaración de la clase es similar a la de una clase no genérica, salvo que el nombre de la clase va seguido de una sección de parámetros de tipo. En este caso, el parámetro de tipo E, que será el que manipulará la pila. Al igual que con los métodos genéricos, la sección de parámetros de tipo puede tener uno o más parámetros separados por comas. El parámetro de tipo E se utiliza en la declaración de la clase Pila para representar el tipo del elemento.