miércoles, 18 de noviembre de 2015

Applets y Java
Los applets son pequeños programas escritos en lenguaje Java, diseñados para ser ejecutados desde internet, que podemos colocar en nuestro servidor, junto con el resto de ficheros que componen un sitio Web (documentos HTML, ficheros de imagen, sonido, etc.) para lograr múltiples efectos con texto, imágenes, sonidos, etc.
Lenguaje Java
El lenguaje Java no está diseñado solamente para crear applets que corren en la ventana del navegador. Java es un lenguaje de programación orientado a objetos que ha sido creado y desarrollado por la compañía Sun Microsystems. Es independiente de cualquier plataforma y puede ejecutarse en cualquier ordenador que tenga un navegador compatible con Java ( Netscape 2.x o superior, Explorer 3.0 o superior, o HotJava).
El lenguaje Java es a la vez compilado e interpretado. Con el compilador se convierte el código fuente que reside en archivos cuya extensión es .java, a un conjunto de instrucciones que recibe el nombre de bytecodes que se guardan en un archivo cuya extensión es .class. Estas instrucciones son independientes del tipo de ordenador. El intérprete ejecuta cada una de estas instrucciones en un ordenador específico (Windows, Macintosh, etc). Solamente es necesario, por tanto, compilar una vez el programa, pero se interpreta cada vez que se ejecuta en un ordenador.
Cada intérprete Java es una implementación de la Máquina Virtual Java (JVM). Los bytecodes posibilitan el objetivo de "write once, run anywhere", de escribir el programa una vez y que se pueda correr en cualquier plataforma que disponga de una implementación de la JVM. Por ejemplo, el mismo programa Java puede correr en Windows 98, Solaris, Macintosh, etc. Java es, por tanto, algo más que un lenguaje, ya que la palabra Java se refiere a dos cosas inseparables: el lenguaje que nos sirve para crear programas y la Máquina Virtual Java que sirve para ejecutarlos.
Como vemos en la figura, el API de Java y la Máquina Virtual Java forman una capa intermedia (Java platform) que aísla el programa Java de las especificidades del hardware (hardware-based platform).
Java es un lenguaje de propósito general, de alto nivel, y orientado a objetos. Java es un lenguaje de programación orientado a objetos puro, en el sentido de que no hay ninguna variable, función o constante que no esté dentro de una clase. Se accede a los miembros dato y las funciones miembro a través de los objetos y de las clases. Por razones de eficiencia, se han conservado los tipos básicos de datos, int, float, double, char, etc, similares a los del lenguaje C/C++.
Los tipos de programas más comunes que se pueden hacer con Java son los applets (se ejecutan en el navegador de la máquina cliente) y las aplicaciones (programas que se ejecutan directamente en la JVM). Otro tipo especial de programa se denomina servlet que es similar a los applets pero se ejecutan en los servidores Java. La API de Java es muy rica, está formada un conjunto de paquetes de clases que le proporcionan una gran funcionalidad.
Máquina Virtual Java
La Máquina Virtual Java (JVM) es el entorno en el que se ejecutan los programas Java, su misión principal es la de garantizar la portabilidad de las aplicaciones Java. Define esencialmente un ordenador abstracto y especifica las instrucciones (bytecodes) que este ordenador puede ejecutar. El intérprete Java específico ejecuta las instrucciones que se guardan en los archivos cuya extensión es .class. Las tareas principales de la JVM son las siguientes:
  • Reservar espacio en memoria para los objetos creados.
  • Liberar la memoria no usada (garbage collection).
  • Asignar variables a registros y pilas.
  • Llamar al sistema huésped para ciertas funciones, como los accesos a los dispositivos.
  • Vigilar el cumplimiento de las normas de seguridad de las aplicaciones Java.
  • Las referencias a arrays son verificadas en el momento de la ejecución del programa.
  • La JVM gestiona automáticamente el uso de la memoria, de modo que no queden huecos.
Por ejemplo, cuando el navegador encuentra una página web con un applet, pone en marcha la JVM y proporciona la información que aparece en la etiqueta <applet> ... </applet>. El cargador de clases dentro de la JVM ve que clases necesita el applet. Dentro del proceso de carga, las clases se examinan mediante un verificador que asegura que las clases contienen código válido y no malicioso. Finalmente, se ejecuta el applet.
¿Cómo colocar applets en páginas web?
Cuando visitas o descargas una página web debes tener en cuenta que aunque los applets insertos en ella estarán definidos en su código, la página no funcionara offline ya que necesita un paquete cerrado (archivo class) que procede de la compilación del correspondiente archivo ".java". 
Por eso para colocar adecuadamente algún applet en una página web debes proceder asi:
  1. Descarga los archivos necesarios para que el applet funcione. El archivo más importante es el archivo " .class" que es donde va compilado el programa java.
  2. Inserta el código del applet entre las etiquetas <body>..</body> del documento HTML.
  3. Si el archivo " .class" no lo colocas en el mismo directorio que el documento HTML que contiene el código del applet debes indicar la ruta hacia el mismo mediante el atributo codebase dentro de la etiqueta <applet>.
  4. La forma más sencilla de utilizar un applet es guardar el archivo " .class" en el mismo directorio que el documento HTML que contiene su código. 


A CONTINUACION UN EJEMPLO GENERAL PARA LA BUENA COMPRENSIÓN DEL USO DE LAS APPLETS

Ahora voy a construir un applet muy sencillo para mostrar cómo lograr resultados similares al arriba mostrado.  El primer paso para crear applets en Java con Netbeans es iniciar Netbeans IDE y crear un nuevo proyecto común y corriente. En mi caso lo he llamado simplemente “Applet”.
Crear Applets en Java con Netbeans
Ahora es necesario agregar al paquete applet un nuevo formulario, del tipoJAppletForm.
Crear Applets en Java con Netbeans
Se le asigna un nombre sencillo. En mi caso lo llamaré Window.
Netbeans
Ahora tenemos un lienzo donde podemos colocar los controles que queremos y hacer nuestra interfaz tal como la necesitamos. Yo colocaré un botón que al presionarse haga que un jLabel muestre el mensaje “Hola Mundo”.
Crear Applets en Java con Netbeans

La programación es idéntica a la de una aplicación de escritorio.
Crear Applets en Java con Netbeans
Programación del Botón

Ahora estamos listos para compilar el código. Lo que vamos a hacer es agregar un archivo HTML a nuestro proyecto para poder visualizar nuestro applet desde el navegador.
Crear Applets en Java con Netbeans
El archivo debe ser llamado index.
Crear Applets en Java con Netbeans
En el documento HTML es necesario agregar una sentencia que debe poseer la siguiente sintaxis:
[codesyntax lang=”php”]
[/codesyntax]
En mi caso, mi sintaxis sería la siguiente:

Crear Applets en Java con Netbeans
Sintaxis en HTML para insertar el applet
Nótese que el archivo index.html no se debe colocar en el mismo paquete que el jAppletFrame sino que se debe crear un paquete a parte como se muestra en la imagen. Con esto, podemos probar el funcionamiento de nuestro applet. Solo le compilamos el proyecto y vamos a la carpeta raíz del programa.
Netbeans
Se ejecuta el archivo index.html y nos aparece el applet.
Crear Applets en Java con Netbeans
Crear Applets en Java con Netbeans
Crear Applets en Java con Netbeans


Estos applets se pueden subir a cualquier página web o blog y ser utilizados por cualquier usuario que acceda a dicha dirección web.

martes, 17 de noviembre de 2015

programacion orientado a objetos

La programación orientada a objetos se basa en la programación de clases; a diferencia de la programación estructurada, que está centrada en las funciones.
Una clase es un molde del que luego se pueden crear múltiples objetos, con similares características.
Una clase es una plantilla (molde), que define atributos (variables) y métodos (funciones)
La clase define los atributos y métodos comunes a los objetos de ese tipo, pero luego, cada objeto tendrá sus propios valores y compartirán las mismas funciones.
Debemos crear una clase antes de poder crear objetos (instancias) de esa clase. Al crear un objeto de una clase, se dice que se crea una instancia de la clase o un objeto propiamente dicho.
La estructura de una clase es:
class [nombre de la clase] {
  [atributos o variables de la clase]
  [métodos o funciones de la clase]
  [main]
}

Problema 1:

Confeccionar una clase que permita carga el nombre y la edad de una persona. Mostrar los datos cargados. Imprimir un mensaje si es mayor de edad (edad>=18)

Programa:

import java.util.Scanner;
public class Persona {
    private Scanner teclado;
    private String nombre;
    private int edad;
    
    public void inicializar() {
        teclado=new Scanner(System.in);
        System.out.print("Ingrese nombre:");
        nombre=teclado.next();
        System.out.print("Ingrese edad:");
        edad=teclado.nextInt();
    }
    
    public void imprimir() {
        System.out.println("Nombre:"+nombre);
        System.out.println("Edad:"+edad);
    }
    
    public void esMayorEdad() {
        if (edad>=18) {
            System.out.print(nombre+" es mayor de edad.");
        } else {
            System.out.print(nombre+" no es mayor de edad.");
        }
    }
    
    public static void main(String[] ar) {
        Persona persona1;
        persona1=new Persona();
        persona1.inicializar();
        persona1.imprimir();
        persona1.esMayorEdad();
    }
}
El nombre de la clase debe hacer referencia al concepto (en este caso la hemos llamado Persona):
public class Persona {
Los atributos los definimos dentro de la clase pero fuera de la main:
    private Scanner teclado;
    private String nombre;
    private int edad;
Veremos más adelante que un atributo es normalmente definido con la cláusula private (con esto no permitimos el acceso al atributo desde otras clases)
A los atributos se tiene acceso desde cualquier función o método de la clase (salvo la main)
Luego de definir los atributos de la clase debemos declarar los métodos o funciones de la clase. La sintaxis es parecida a la main (sin la cláusula static):
    public void inicializar() {
        teclado=new Scanner(System.in);
        System.out.print("Ingrese nombre:");
        nombre=teclado.next();
        System.out.print("Ingrese edad:");
        edad=teclado.nextInt();
    }
En el método inicializar (que será el primero que deberemos llamar desde la main) creamos el objeto de la clase Scanner y cargamos por teclado los atributos nombre y edad. Como podemos ver el método inicializar puede hacer acceso a los tres atributos de la clase Persona.
El segundo método tiene por objetivo imprimir el contenido de los atributos nombre y edad (los datos de los atributos se cargaron al ejecutarse previamente el método inicializar:
    public void imprimir() {
        System.out.println("Nombre:"+nombre);
        System.out.println("Edad:"+edad);
    }
El tercer método tiene por objetivo mostrar un mensaje si la persona es mayor o no de edad:
    public void esMayorEdad() {
        if (edad>=18) {
            System.out.print(nombre+" es mayor de edad.");
        } else {
            System.out.print(nombre+" no es mayor de edad.");
        }
    }
Por último en la main declaramos un objeto de la clase Persona y llamamos a los métodos en un orden adecuado:
    public static void main(String[] ar) {
        Persona persona1;
        persona1=new Persona();
        persona1.inicializar();
        persona1.imprimir();
        persona1.esMayorEdad();
    }
Persona persona1; //Declaración del objeto
persona1=new Persona(); //Creación del objeto
persona1.inicializar(); //Llamada de un método

Problema 2:

Desarrollar un programa que cargue los lados de un triángulo e implemente los siguientes métodos: inicializar los atributos, imprimir el valor del lado mayor y otro método que muestre si es equilátero o no.

Programa:

import java.util.Scanner;
public class Triangulo {
    private Scanner teclado;
    private int lado1,lado2,lado3;
    
    public void inicializar() {
        teclado=new Scanner(System.in);
        System.out.print("Medida lado 1:");
        lado1=teclado.nextInt();
        System.out.print("Medida lado 2:");
        lado2=teclado.nextInt();
        System.out.print("Medida lado 3:");
        lado3=teclado.nextInt();       
    }
    
    public void ladoMayor() {
     System.out.print("Lado mayor:");
        if (lado1>lado2 && lado1>lado3) {
            System.out.println(lado1);
        } else {
            if (lado2>lado3) {
                System.out.println(lado2);
            } else {
                System.out.println(lado3);
            }
        }
    }
    
    public void esEquilatero() {
        if (lado1==lado2 && lado1==lado3) {
            System.out.print("Es un triángulo equilátero");
        } else {
            System.out.print("No es un triángulo equilátero");            
        }
    }

    public static void main(String []ar) {
        Triangulo triangulo1=new Triangulo();
        triangulo1.inicializar();
        triangulo1.ladoMayor();
        triangulo1.esEquilatero();
    }
}
Todos los problemas que requieran la entrada de datos por teclado debemos definir un atributo de la clase Scanner:
    private Scanner teclado;
Este problema requiere definir tres atributos de tipo entero donde almacenamos los valores de los lados del triángulo:
    private int lado1,lado2,lado3;
El primer método que deberá llamarse desde la main es el inicializar donde creamos el objeto de la clase Scanner y cargamos los tres atributos por teclado:
    public void inicializar() {
        teclado=new Scanner(System.in);
        System.out.print("Medida lado 1:");
        lado1=teclado.nextInt();
        System.out.print("Medida lado 2:");
        lado2=teclado.nextInt();
        System.out.print("Medida lado 3:");
        lado3=teclado.nextInt();       
    }
El método ladoMayor muestra el valor mayor de los tres enteros ingresados:
    public void ladoMayor() {
     System.out.print("Lado mayor:");
        if (lado1>lado2 && lado1>lado3) {
            System.out.println(lado1);
        } else {
            if (lado2>lado3) {
                System.out.println(lado2);
            } else {
                System.out.println(lado3);
            }
        }
    }
Como podemos observar cuando un problema se vuelve más complejo es más fácil y ordenado separar los distintos algoritmos en varios métodos y no codificar todo en la main.
El último método de esta clase verifica si los tres enteros ingresados son iguales:
    public void esEquilatero() {
        if (lado1==lado2 && lado1==lado3) {
            System.out.print("Es un triángulo equilátero");
        } else {
            System.out.print("No es un triángulo equilátero");            
        }
    }
En la main creamos un objeto de la clase Triangulo y llamamos los métodos respectivos:
    public static void main(String []ar) {
        Triangulo triangulo1=new Triangulo();
        triangulo1.inicializar();
        triangulo1.ladoMayor();
        triangulo1.esEquilatero();
    }

Problema 3:

Desarrollar una clase que represente un punto en el plano y tenga los siguientes métodos: cargar los valores de x e y, imprimir en que cuadrante se encuentra dicho punto (concepto matemático, primer cuadrante si x e y son positivas, si x<0 e y>0 segundo cuadrante, etc.)

Programa:

import java.util.Scanner;
public class Punto {
    private Scanner teclado;
    int x,y;
    
    public void inicializar() {
        teclado=new Scanner(System.in);
        System.out.print("Ingrese coordenada x :");
        x=teclado.nextInt();
        System.out.print("Ingrese coordenada y :");
        y=teclado.nextInt();
    }
    
    void imprimirCuadrante() {
        if (x>0 && y>0) {
            System.out.print("Se encuentra en el primer cuadrante.");
        } else {
            if (x<0 && y>0) {
                System.out.print("Se encuentra en el segundo cuadrante.");
            } else {
                if (x<0 && y<0) {
                    System.out.print("Se encuentra en el tercer cuadrante.");
                } else {
                    if (x>0 && y<0) {
                        System.out.print("Se encuentra en el cuarto cuadrante.");
                    } else {
                        System.out.print("El punto no está en un cuadrante.");
                    }
                }
            }
        }
    }
    
    public static void main(String[] ar) {
        Punto punto1;
        punto1=new Punto();
        punto1.inicializar();
        punto1.imprimirCuadrante();
    }
}
Definimos tres atributos (el objeto de la clase Scanner y los dos enteros donde almacenamos la coordenada x e y del punto:
    private Scanner teclado;
    int x,y;
El método inicializar crea el objeto de la clase Scanner y pide cargar las coordenadas x e y:
    public void inicializar() {
        teclado=new Scanner(System.in);
        System.out.print("Ingrese coordenada x :");
        x=teclado.nextInt();
        System.out.print("Ingrese coordenada y :");
        y=teclado.nextInt();
    }
El segundo método mediante un conjunto de if verificamos en que cuadrante se encuentra el punto ingresado:
    void imprimirCuadrante() {
        if (x>0 && y>0) {
            System.out.print("Se encuentra en el primer cuadrante.");
        } else {
            if (x<0 && y>0) {
                System.out.print("Se encuentra en el segundo cuadrante.");
            } else {
                if (x<0 && y<0) {
                    System.out.print("Se encuentra en el tercer cuadrante.");
                } else {
                    if (x>0 && y<0) {
                        System.out.print("Se encuentra en el cuarto cuadrante.");
                    } else {
                        System.out.print("El punto no está en un cuadrante.");
                    }
                }
            }
        }
    }
La main no tiene grandes diferencias con los problemas realizados anteriormente, declaramos un objeto de la clase Punto, creamos el objeto mediante el operador new y seguidamente llamamos a los métodos inicializar e imprimirCuadrante en ese orden:
    public static void main(String[] ar) {
        Punto punto1;
        punto1=new Punto();
        punto1.inicializar();
        punto1.imprimirCuadrante();
    }

domingo, 8 de noviembre de 2015

Tipos de datos abstractos
TDA lista
Una lista se define como una serie de N elementos E1, E2, ..., EN, ordenados de manera consecutiva, es decir, el elemento Ek (que se denomina elemento k-ésimo) es previo al elemento Ek+1. Si la lista contiene 0 elementos se denomina como lista vacía.
Las operaciones que se pueden realizar en la lista son: insertar un elemento en la posición k, borrar el k-ésimo elemento, buscar un elemento dentro de la lista y preguntar si la lista esta vacía.
Una manera simple de implementar una lista es utilizando un arreglo. Sin embargo, las operaciones de inserción y borrado de elementos en arreglos son ineficientes, puesto que para insertar un elemento en la parte media del arreglo es necesario mover todos los elementos que se encuentren delante de él, para hacer espacio, y al borrar un elemento es necesario mover todos los elementos para ocupar el espacio desocupado. Una implementación más eficiente del TDA se logra utilizando listas enlazadas.
A continuación se presenta una implementación en Java del TDA utilizando listas enlazadas y sus operaciones asociadas:
estaVacia(): devuelve verdadero si la lista esta vacía, falso en caso contrario.
insertar(x, k): inserta el elemento x en la k-ésima posición de la lista.
buscar(x): devuelve la posición en la lista del elemento x.
buscarK(k): devuelve el k-ésimo elemento de la lista.
eliminar(x): elimina de la lista el elemento x.
En la implementación con listas enlazadas es necesario tener en cuenta algunos detalles importantes: si solamente se dispone de la referencia al primer elemento, el añadir o remover en la primera posición es un caso especial, puesto que la referencia a la lista enlazada debe modificarse según la operación realizada. Además, para eliminar un elemento en particular es necesario conocer el elemento que lo antecede, y en este caso, ¿qué pasa con el primer elemento, que no tiene un predecesor?
Para solucionar estos inconvenientes se utiliza la implementación de lista enlazada con nodo cabecera. Con esto, todos los elementos de la lista tendrán un elemento previo, puesto que el previo del primer elemento es la cabecera. Una lista vacía corresponde, en este caso, a una cabecera cuya referencia siguiente es null.

Los archivos NodoLista.java, IteradorLista.java y Lista.java contienen una implementación completa del TDA lista. La clase NodoLista implementa los nodos de la lista enlazada, la clase Lista implementa las operaciones de la lista propiamente tal, y la clase IteradorLista implementa objetos que permiten recorrer la lista y posee la siguiente interfaz:
avanzar(): avanza el iterador al siguiente nodo de la lista.
obtener(): retorna el elemento del nodo en donde se encuentra el iterador.
Costo de las operaciones en tiempo:
Insertar/eliminar elemento en k-ésima posición: O(k) (¿Se puede hacer en O(1)?).
Buscar elemento x: O(N) (promedio).
TDA pila
Una pila (stack o pushdown en inglés) es una lista de elementos de la cual sólo se puede extraer el último elemento insertado. La posición en donde se encuentra dicho elemento se denomina tope de la pila. También se conoce a las pilas como listas LIFO (LAST IN - FIRST OUT: el último que entra es el primero que sale).

La interfaz de este TDA provee las siguientes operaciones:
apilar(x): inserta el elemento x en el tope de la pila (push en inglés).
desapilar(): retorna el elemento que se encuentre en el tope de la pila y lo elimina de ésta (pop en inglés).
tope(): retorna el elemento que se encuentre en el tope de la pila, pero sin eliminarlo de ésta (top en inglés).
estaVacia(): retorna verdadero si la pila no contiene elementos, falso en caso contrario (isEmpty en inglés).
Nota: algunos autores definen desapilar como sacar el elemento del tope de la pila sin retornarlo.
Implementación del TDA pila
A continuación se muestran dos maneras de implementar una pila: utilizando un arreglo y utilizando una lista enlazada. En ambos casos el costo de las operaciones es de O(1).
Implementación utilizando arreglos
Para implementar una pila utilizando un arreglo, basta con definir el arreglo del tipo de dato que se almacenará en la pila. Una variable de instancia indicará la posición del tope de la pila, lo cual permitirá realizar las operaciones de inserción y borrado, y también permitirá saber si la pila esta vacía, definiendo que dicha variable vale -1 cuando no hay elementos en el arreglo.
class PilaArreglo
{
  private Object[] arreglo;
  private int tope;
  private int MAX_ELEM=100; // maximo numero de elementos en la pila

  public PilaArreglo()
  {
    arreglo=new Object[MAX_ELEM];
    tope=-1; // inicialmente la pila esta vacía
  }

  public void apilar(Object x)
  {
    if (tope+1<MAX_ELEM) // si esta llena se produce OVERFLOW
    {
      tope++;
      arreglo[tope]=x;
    }
  }

  public Object desapilar()
  {
    if (!estaVacia()) // si esta vacia se produce UNDERFLOW
    {
      Object x=arreglo[tope];
      tope--;
      return x;
    }
  }

  public Object tope()
  {
    if (!estaVacia()) // si esta vacia es un error
    {
      Object x=arreglo[tope];
      return x;
    }
  }

  public boolean estaVacia()
  {
    if (tope==-1)
    {
      return true;
    }
    else
    {
      return false;
    }
  }
}
El inconveniente de esta implementación es que es necesario fijar de antemano el número máximo de elementos que puede contener la pila, MAX_ELEM, y por lo tanto al apilar un elemento es necesario controlar que no se inserte un elemento si la pila esta llena. Sin embargo, en Java es posible solucionar este problema creando un nuevo arreglo más grande que el anterior, el doble por ejemplo, y copiando los elementos de un arreglo a otro:
public void apilar(Object x)
{
  if (tope+1<MAX_ELEM) // si esta llena se produce OVERFLOW
  {
    tope++;
    arreglo[tope]=x;
  }
  else
  {
    MAX_ELEM=MAX_ELEM*2;
    Object[] nuevo_arreglo=new Object[MAX_ELEM];
    for (int i=0; i<arreglo.length; i++)
    {
      nuevo_arreglo[i]=arreglo[i];
    }
    tope++;
    nuevo_arreglo[tope]=x;
    arreglo=nuevo_arreglo;
  }
}
Implementación utilizando listas enlazadas
En este caso no existe el problema de tener que fijar el tamaño máximo de la pila (aunque siempre se está acotado por la cantidad de memoria disponible!). La implementación es bastante simple: los elementos siempre se insertan al principio de la lista (apilar) y siempre se extrae el primer elemento de la lista (desapilar y tope), por lo que basta con tener una referencia al principio de la lista enlazada. Si dicha referencia es null, entonces la pila esta vacía.
class PilaLista
{
  private NodoLista lista;

  public PilaLista()
  {
    lista=null;
  }

  public void apilar(Object x)
  {
    lista=new NodoLista(x, lista);
  }

  public Object desapilar() // si esta vacia se produce UNDERFLOW
  {
    if (!estaVacia())
    {
      Object x=lista.elemento;
      lista=lista.siguiente;
      return x;
    }
  }

  public Object tope()
  {
    if (!estaVacia()) // si esta vacia es un error
    {
      Object x=lista.elemento;
      return x;
    }
  }

  public boolean estaVacia()
  {
    return lista==null;
  }
}
Dependiendo de la aplicación que se le de a la pila es necesario definir que acción realizar en caso de que ocurra overflow (rebalse de la pila) o underflow (intentar desapilar cuando la pila esta vacía). Java posee un mecanismo denominado excepciones, que permite realizar acciones cuando se producen ciertos eventos específicos (como por ejemplo overflow o underflow en una pila).
En ambas implementaciones el costo de las operaciones que provee el TDA tienen costo O(1).
Ejemplo de uso: eliminación de recursividad
Suponga que una función F realiza un llamado recursivo dentro de su código, lo que se ilustra en la siguiente figura:

Si la llamada recursiva es lo último que hace la función F, entonces dicha llamada se puede substituir por un ciclo while. Este caso es conocido como tail recursion y en lo posible hay que evitarlo en la programación, ya que cada llamada recursiva ocupa espacio en la memoria del computador, y en el caso del tail recursion es muy simple eliminarla. Por ejemplo:
void imprimir(int[] a, int j) // versión recursiva
{
  if (j<a.length)
  {
    System.out.println(a[j]);
    imprimir(a, j+1); // tail recursion
  }
}

void imprimir(int[] a, int j) // versión iterativa
{
  while (j<a.length)
  {
    System.out.println(a[j]);
    j=j+1;
  }
}
En el caso general, cuando el llamado recursivo se realiza en medio de la función F, la recursión se puede eliminar utilizando una pila.
Por ejemplo: recorrido en preorden de un arbol binario.
// "raiz" es la referencia a la raiz del arbol
// llamado inicial: preorden(raiz)

// version recursiva

void preorden(Nodo nodo)
{
  if (nodo!=null)
  {
    System.out.print(nodo.elemento);
    preorden(nodo.izq);
    preorden(nodo.der);
  }
}

// primera version iterativa

void preorden(Nodo nodo)
{
  Nodo aux;
  Pila pila=new Pila(); // pila de nodos
  pila.apilar(nodo);
  while(!pila.estaVacia()) // mientras la pila no este vacia
  {
    aux=pila.desapilar();
    if (aux!=null)
    {
      System.out.print(aux.elemento);
      // primero se apila el nodo derecho y luego el izquierdo
      // para mantener el orden correcto del recorrido
      // al desapilar los nodos
      pila.apilar(aux.der);
      pila.apilar(aux.izq);
    }
  }
}

// segunda version iterativa
// dado que siempre el ultimo nodo apilado dentro del bloque if es
// aux.izq podemos asignarlo directamente a aux hasta que éste sea
// null, es decir, el bloque if se convierte en un bloque while
// y se cambia el segundo apilar por una asignacion de la referencia

void preorden(Nodo nodo)
{
  Nodo aux;
  Pila pila=new Pila(); // pila de nodos
  pila.apilar(nodo);
  while(!pila.estaVacia()) // mientras la pila no este vacia
  {
    aux=pila.desapilar();
    while (aux!=null)
    {
      System.out.print(aux.elemento);
      pila.apilar(aux.der);
      aux=aux.izq;
    }
  }
}
Si bien los programas no recursivos son más eficientes que los recursivos, la eliminación de recursividad (excepto en el caso de tail recursion) le quita claridad al código del programa. Por lo tanto:
A menudo es conveniente eliminar el tail recursion.
Un método recursivo es menos eficiente que uno no recursivo, pero sólo en pocas oportunidades vale la pena eliminar la recursión.
TDA cola
Una cola (queue en inglés) es una lista de elementos en donde siempre se insertan nuevos elementos al final de la lista y se extraen elementos desde el inicio de la lista. También se conoce a las colas como listas FIFO (FIRST IN - FIRST OUT: el primero que entra es el primero que sale).

Las operaciones básicas en una cola son:
encolar(x): inserta el elemento x al final de la cola (enqueue en inglés).
sacar(): retorna el elemento que se ubica al inicio de la cola (dequeue en inglés).
estaVacia(): retorna verdadero si la cola esta vacía, falso en caso contrario.
Al igual que con el TDA pila, una cola se puede implementar tanto con arreglos como con listas enlazadas. A continuación se verá la implementación usando un arreglo.
Las variables de instancia necesarias en la implementación son:
primero: indica el índice de la posición del primer elemento de la cola, es decir, la posición el elemento a retornar cuando se invoque sacar.
ultimo: indica el índice de la posición de último elemento de la cola. Si se invoca encolar, el elemento debe ser insertado en el casillero siguiente al que indica la variable.
numElem: indica cuántos elementos posee la cola. Definiendo MAX_ELEM como el tamaño máximo del arreglo, y por lo tanto de la cola, entonces la cola esta vacía si numElem==0 y está llena si numElem==MAX_ELEM.

Un detalle faltante es el siguiente: ¿qué pasa si la variable ultimo sobrepasa el rango de índices del arreglo? Esto se soluciona definiendo que si después de insertar un elemento el índice ultimo == MAX_ELEM, entonces se asigna ultimo = 0 , y los siguientes elementos serán insertados al comienzo del arreglo. Esto no produce ningún efecto en la lógica de las operaciones del TDA, pues siempre se saca el elemento referenciado por el índice primero, aunque en valor absoluto primero > ultimo. Este enfoque es conocido como implementación con arreglo circular, y la forma más fácil de implementarlo es haciendo la aritmética de subíndices módulo MAX_ELEM.

class ColaArreglo
{
  private Object[] arreglo;
  private int primero, ultimo, numElem;
  private int MAX_ELEM=100; // maximo numero de elementos en la cola

  public ColaArreglo()
  {
    arreglo=new Object[MAX_ELEM];
    primero=0;
    ultimo=-1;
    numElem=0;
  }

  public void encolar(Object x)
  {
    if (numElem<=MAX_ELEM) // si esta llena se produce OVERFLOW
    {
      ultimo=(ultimo+1)%MAX_ELEM;
      arreglo[ultimo]=x;
      numElem++;
    }
  }

  public Object sacar()
  {
    if (!estaVacia()) // si esta vacia se produce UNDERFLOW
    {
      Object x=arreglo[primero];
      primero=(primero+1)%MAX_ELEM;
      numElem--;
      return x;
    }
  }

  public boolean estaVacia()
  {
    return num_elem==0;
  }
}
Nuevamente en este caso, dependiendo de la aplicación, se debe definir qué hacer en caso de producirse OVERFLOW o UNDERFLOW.
Con esta implementación, todas las operaciones del TDA cola tienen costo O(1).
TDA Cola de Prioridad
Una cola de prioridad es un tipo de datos abstracto que almacena un conjunto de datos que poseen una llave perteneciente a algún conjunto ordenado, y permite insertar nuevos elementos y extraer el máximo (o el mínimo, en caso de que la estructura se organice con un criterio de orden inverso).
Es frecuente interpretar los valores de las llaves como prioridades, con lo cual la estructura permite insertar elementos de prioridad cualquiera, y extraer el de mejor prioridad.
Dos formas simples de implementar colas de prioridad son:
Una lista ordenada:
o Inserción: O(n)
o Extracción de máximo: O(1)
Una lista desordenada:
o Inserción: O(1)
o Extracción de máximo: O(n)
Heaps
Un heap es un árbol binario de una forma especial, que permite su almacenamiento en un arreglo sin usar punteros.
Un heap tiene todos sus niveles llenos, excepto posiblemente el de más abajo, y en este último los nodos están lo más a la izquierda posible.
Ejemplo:

La numeración por niveles (indicada bajo cada nodo) son los subíndices en donde cada elemento sería almacenado en el arreglo. En el caso del ejemplo, el arreglo sería:

La característica que permite que un heap se pueda almacenar sin punteros es que, si se utiliza la numeración por niveles indicada, entonces la relación entre padres e hijos es:
Hijos del nodo j = {2*j, 2*j+1}
Padre del nodo k = floor(k/2)
Un heap puede utilizarse para implementar una cola de prioridad almacenando los datos de modo que las llaves estén siempre ordenadas de arriba a abajo (a diferencia de un árbol de búsqueda binaria, que ordena sus llaves de izquierda a derecha). En otras palabras, el padre debe tener siempre mayor prioridad que sus hijos (ver ejemplo).
Implementación de las operaciones básicas
Inserción:
La inserción se realiza agregando el nuevo elemento en la primera posición libre del heap, esto es, el próximo nodo que debería aparecer en el recorrido por niveles o, equivalentemente, un casillero que se agrega al final del arreglo.
Después de agregar este elemento, la forma del heap se preserva, pero la restricción de orden no tiene por qué cumplirse. Para resolver este problema, si el nuevo elemento es mayor que su padre, se intercambia con él, y ese proceso se repite mientras sea necesario. Una forma de describir esto es diciendo que el nuevo elemento "trepa" en el árbol hasta alcanzar el nivel correcto según su prioridad.

El siguiente trozo de programa muestra el proceso de inserción de un nuevo elemento x:
    a[++n]=x;
    for(j=n; j>1 && a[j]>a[j/2]; j/=2)
      { # intercambiamos con el padre
        t=a[j];
        a[j]=a[j/2];
        a[j/2]=t;
      }
El proceso de inserción, en el peor caso, toma un tiempo proporcional a la altura del árbol, esto es, O(log n).
Extracción del máximo
El máximo evidentemente está en la raíz del árbol (casillero 1 del arreglo). Al sacarlo de ahí, podemos imaginar que ese lugar queda vacante. Para llenarlo, tomamos al último elemento del heap y lo trasladamos al lugar vacante. En caso de que no esté bien ahí de acuerdo a su prioridad (¡que es lo más probable!), lo hacemos descender intercambiándolo siempre con el mayor de sus hijos. Decimos que este elemento "se hunde" hasta su nivel de prioridad.

El siguiente trozo de programa implementa este algoritmo:
    m=a[1];  # La variable m lleva el máximo
    a[1]=a[n--];  # Movemos el último a la raíz y achicamos el heap
    j=1;
    while(2*j<n) # mientras tenga algún hijo
      {
        k=2*j; # el hijo izquierdo
        if(k+1<=n && a[k+1]>a[k])
            k=k+1;  # el hijo derecho es el mayor
        if(a[j]>a[k])
            break;  # es mayor que ambos hijos
        t=a[j];
        a[j]=a[k];
        a[k]=t;
        j=k;   # lo intercambiamos con el mayor hijo
      }
Este algoritmo también demora un tiempo proporcional a la altura del árbol en el peor caso, esto es, O(log n).

Bueno para conectarnos a una base de datos ORACLE necesitamos descargarnos el driver directamente desde la pagina de oracle que nos servira para obtener la funcionalidad, yo descarge el ojdbc6.jar es como el tercero en el siguiente link 

http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html 

Bueno esta de mas decir q necesitamos crear un proyecto en netbeans 

Conectar Base de datos de ORacle con aplicacion JAVA 

Despues de descargarnos el driver lo debemos agregar a la carpeta Libraries de nuestro proyecto click derecho>addjar buscamos el jar y ya esta: 

Netbeans y oracle

Bueno manos a la obra  

Para simplificar este tutorial encapsularemos toda la funcionalidad en una sola clase llamada 'funcionalidad', la cual explicaremos paso a paso. 

Como Paso 1 Creamos la conexion a la base de datos : Este paso es importantisimo porque sin esta conexion no podremos establecer una sesion, a continuacion el codigo: 

Este metodo de tipo funcionalidad llamado conectar es el q se encarga de crear dicha conexion, lo primero es meter todo el codigo dentro de un try and catch para solventar o manejar cualquier error que pueda darse, luego:: 


public funcionalidad conectar() { 
    try { 
        Class.forName("oracle.jdbc.OracleDriver"); 

Este es el nombre del driver jdbc que descargamos antes 

String BaseDeDatos = "jdbc:oracle:thin:@localhost:1521:bdb";  

Esta parte es importante ya que aqui es donde le decimos a java a que base de datos se va a conectar (el nombre), la ip del host al que se va a conectar (aunque puede ser el localhost), el puerto por el que escucha el listener, vamos que si trabajan con oracle saben lo que es un listener y por ultimo el nombre de la database q en mi caso es bdb. 
        
        conexion = DriverManager.getConnection(BaseDeDatos, "hr","hr");             
        if (conexion != null) { 
            System.out.println("Conexion exitosa!"); 
        } else { 
            System.out.println("Conexion fallida!"); 
        } 
    } catch (Exception e) { 
        e.printStackTrace(); 
    }        return this; 

En esta parte le indicamos a oracle que usuario vamos a usar para la conexion, nosotros usaremos el usuario HR con la contraseña HR tambien  

PASO 2: Ahora seguimos con la funcionalidad que nos permitira escribir en la base de datos (Insertar, Borrar o hacer update) 

Escribir metodo es un booleano que devuelve true si se realiza con exito la transaccion o false si hay algun error. 

public boolean escribir(String sql) { 


Todo dentro de un try and catch como buena practica de programacion  
y aquie basicamente recivimos un objeto de tipo String que contiene una sentencia sql a la que le daremos formato en la interfaz grafica, luego creamos un objeto de tipo Statement que nos permite escribir borrar o actualizar valores dentro de la base de datos con el hacemos un executeUpdate de nuestra cadena sql y luego un commit, despues cerramos la conexion. 
Si existe algun error devolvemos una cadena de error.. 

try { 
            Statement sentencia; 
            sentencia = getConexion().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            sentencia.executeUpdate(sql); 
            getConexion().commit(); 
            sentencia.close(); 
             
        } catch (SQLException e) { 
            e.printStackTrace(); 
            System.out.print("ERROR SQL"); 
            return false; 
        }         
        return true; 
    } 


PASO 3: ahora vamos a crear el metodo consultar, que no hace otra cosa mas que consultar la base de datos 

Este es un metodo de tipo ResultSet el cual es capas de almacenar consultas sql tipicas con el comando select y siempre recive una sentencia sql y devuelve un objeto de tipo ResultSet que luego se formatea en la interfaz grafica. 


public ResultSet consultar(String sql) { 
        ResultSet resultado = null; 
        try { 
            Statement sentencia; 
            sentencia = getConexion().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            resultado = sentencia.executeQuery(sql); 
             
        } catch (SQLException e) { 
            e.printStackTrace(); 
            return null; 
        }        return resultado; 
    } 


PASO 4 : la interfaz grafica 
Yo la hice sencilla porque la tabla solo tiene 3 campos y es bastante compresiba 

conexion java y oracle