sábado, 25 de mayo de 2019

Capítulo 10 Java Database Connectivity (JDBC)

1. Creación de base de datos, tablas y relaciones

Un buen diseño de base de datos permite mejorar el rendimiento de un sistema, por lo tanto, es fundamental darse el tiempo para diseñar la estructura que debe tener la base de datos.

Un análisis del funcionamiento permitirá controlar y corregir los pequeños problemas que puedan surgir en el sistema. Para realizar el análisis, se puede recurrir a diferentes herramientas externas que darán una visión detallada de cuáles son los patrones de funcionamiento de base de datos y centrarse en aquellos apartados más conflictivos.

Gran parte de las cualidades esperadas en una buena base de datos se basan en su diseño inicial, que constituye los cimientos de cualquier sistema de información actual. El diseño de base de datos debe estar bien definido antes de pasar a otra etapa, dada la dificultad y el coste de realizar cambios en el diseño en etapas posteriores. El diseño de bases de datos se compone de dos fases altamente diferenciadas: diseño lógico y diseño físico.

El diseño lógico consiste en analizar los requerimientos de la empresa y cuáles van a ser los componentes de base de datos, como por ejemplo las tablas y las restricciones. El diseño lógico no tiene en cuenta dónde o cómo van a estar almacenados físicamente los datos.

El uso efectivo de cualquier base de datos se basa en un buen diseño normalizado. Por su parte, la normalización consiste en el proceso de evitar las redundancias de los datos, la actualización, modificación y borrado de datos en múltiples posiciones.

Además, al eliminar la repetición innecesaria de información, se reduce la probabilidad que la base de datos se vuelva inconsistente o contenga errores. Las sentencias básicas para trabajar con base de datos son las siguientes: 

  • SHOW DATABASES;

Muestra las base de datos existentes en MySQL.
  • CREATE DATABASE dbdat;

Crea la base de datos de nombre dbdat.
  • USE dbdat;

Activa la base de datos de nombre dbdat.
  • DROP DATABASE dbdat;

Elimina la base de datos dbdat.

Una vez creada la base de datos, en ella se crearán las tablas que son los repositorios de datos, para lo cual se deberán definir los tipos de datos que se asignarán en estos repositorios. 

Estos tipos de datos pueden ser los siguientes:

  • VARCHAR(número): Tiene un número variable de caracteres, el número que se pone es el número máximo de caracteres que puede tener este número va de 1 hasta 255.
  • CHAR(número) Tiene un número fijo de caracteres va de 1 hasta 255.
  • DATE Tipo fecha (YYYY-MM-DD)-( '1000-01-01' a '9999-12-31').
  • DATETIME Tipo fecha y hora (YYYY-MM-DD HH:MM:SS).
  • INTEGER (INT) Tipo numérico entero (-2147483648 a 2147483647)
  • FLOAT(M, D) Número real de coma flotante M es el número y D los decimales. (-3.402823466E+38 a -1.175494351E-38, 0, y 1.175494351E- 38 a 3.402823466E+38.)
  • DOUBLE(M, D) Número real de doble precisión M es el número y D los decimales. (- 1.7976931348623157E+308 a - 2.2250738585072014E-308, 0, y 2.2250738585072014E-308 a 1.7976931348623157E+308)
  • BLOB Para grandes textos, siendo la longitud máxima de 65535. Con este tipo las búsquedas de texto son sensibles a las mayúsculas.
  • TEXT Para grandes textos, siendo la longitud máxima de 65535. Con este tipo las búsquedas de texto NO son sensibles a las mayúsculas.


1.1 Opciones para las columnas

  • NOT NULL El valor no puede ser nulo en el campo.
  • AUTO_INCREMENT Automáticamente incrementa el número del registro anterior.
  • PRIMARY KEY El PRIMARY KEY es un campo que MySQL usa como índice. Este índice puede hacer cosas como:
    • Hallar rápidamente filas que acierten una cláusula WHERE.
    • Regresar filas de una tabla desde otras tablas cuando se realizan uniones.
    • Esto definitivamente ayudará a agilizar peticiones.
  • KEY Crea una llave secundaria. Se puede encontrar mayor información en la siguiente dirección: http://dev.mysql.com/doc/refman/5.0/es/column-types.html


1.2 Tablas

Una tabla es un conjunto de datos sobre un tema concreto para guardar datos de personas, productos, departamentos, cursos entre otros. Si se usa una tabla independiente para cada tema, se evitará la duplicación de datos y la base de datos resultará más eficiente, al mismo tiempo que se reducirá el riesgo de errores en la entrada de datos. En las tablas, los datos están organizados en filas y columnas.

Las sentencias básicas para trabajar con tablas son las siguientes:
  • SHOW TABLES;

Muestra las tablas de la base de datos activa.
CREATE TABLE personas (idpersona int NOT NULL AUTO_INCREMENT,nombres varchar(40) NOT NULL,apellidos varchar(40) NOT NULL,sexo char(1) NOT NULL, -- Masculino/Femeninoemail varchar(40) NULL,login varchar(10) NOT NULL,password varchar(10) NOT NULL,telefono varchar(20) NULL,nacimiento datetime NULL, -- fecha en que naciódatoAdicional text NULL -- dato adicional de persona);
  • ALTER TABLE personas ADD UNIQUE (login, password);

Altera la tabla personas, para que la unión de login con password no se repita en las siguientes filas.
  • DROP TABLE personas; Elimina la tabla personas.

2. Consultas

El lenguaje estructurado de consultas (SQL) proporciona la sentencia SELECT, la cual permite hacer consultas a una base de datos. La siguiente tabla explica cada una de las partes de la sentencia Select.
  • SELECT

Palabra clave que indica que la sentencia de SQL que se desea ejecutar es de selección.

Ejemplos:
SELECT * FROM tabla – muestra el contenido de toda la tabla
SELECT col1, col2 FROM tabla -- muestra el contenido de columna 1 y 2

  • FROM

Indica la tabla (o tablas) desde la que se desea recuperar los datos. En el caso de que exista más de una tabla se denomina a la consulta "consulta combinada" o "join".
En las consultas combinadas es necesario aplicar una condición de combinación, a través de una cláusula ON. En las consultas combinadas surge el concepto de llave foránea.
Una llave foránea llamada también llave externa es uno o más campos de una tabla que hacen referencia al campo o campos de clave principal de otra tabla, una clave externa indica cómo están relacionadas las tablas.
Los datos en los campos de clave externa y clave principal deben coincidir, aunque los nombres de los campos no sean los mismos. Ejemplos:

SELECT * FROM tabla – muestra el contenido de tabla.

– Muestra el contenido de tabla1 y tabla2.

Si queremos mostrar el contenido de dos tablas.

Siendo estas:

tabla1
campo 1: idtabla1
campo 2:colx
tabla 2
campo 1: idtabla2
campo 2: idtabla1 ( llave foránea)
campo2 coly
SELECT tabla1.colx, tabla2.coly FROM tabla1
INNER JOIN tabla2
ON tabla1.idtabla1= tabla2.idtabla2 (unión realizada por la llave foránea)

  • WHERE

Especifica una condición que debe cumplirse para que los datos sean
devueltos por la consulta. Además, admiten los operadores lógicos
AND y OR.
Ejemplos:
– muestra fila de id=5
SELECT * FROM tabla WHERE id=5
– muestra filas cuyas edades estén entre [18, 65]
SELECT * FROM tabla WHERE edad>17 AND edad<66

  • GROUP BY

Especifica la agrupación que se da a los datos. Se usa siempre en combinación con funciones agregadas.
Ejemplo:
Tabla alumnos
campo1: idalumno
campo 2: nombre
campo 3: apellido
Tabla notas
campo 1: idnota
campo 2: idalumno
campo 3: tipo nota
– muestra lista de alumnos y promedios
SELECT alumnos.alumno, AVG(notas.nota)
FROM alumnos
INNER JOIN notas
ON alumnos.idalumno= notas.idnota
GROUP BY alumnos
  • HAVING

Especifica una condición que debe cumplirse para que los datos sean devueltos por la consulta. Su funcionamiento es similar al de WHERE, pero aplicado al conjunto de resultados devueltos por la consulta. Debe aplicarse siempre junto a GROUP BY y la condición debe estar referida a los campos contenidos en ella.

Ejemplo:
– Muestra lista de alumnos y promedios mayor o igual a 11.
SELECT alumnos.alumno, AVG(notas.nota)
FROM alumnos
INNER JOIN notas
ON alumnos.id= notas.id
GROUP BY alumnos
HAVING AVG(notas.nota)>=11

  • ORDER BY

Presenta el resultado ordenado por las columnas indicadas. El orden puede expresarse con ASC (orden ascendente) y DESC (orden descendente). El valor predeterminado es ASC.
Ejemplo:
– Lista de alumnos y promedios mayor o igual a 11 en orden de
mérito. 
SELECT alumnos.alumno, AVG(notas.nota)
FROM alumnos
INNER JOIN notas
ON alumnos.id= notas.id
GROUP BY alumnos
HAVING AVG(notas.nota)>=11
ORDER BY 2 DESC

3. Inserción, eliminación y actualización

INSERT

La sentencia INSERT permite insertar valores dentro de una tabla y tiene dos formas importantes a considerar.
Ejemplo:
Insertamos datos en la tabla Alumnos.
Tabla Alumnos
campo 1: nombre
campo 2: dirección
campo 3: teléfono
campo 4: nacimiento
campo 5 : categoría (acepta null)
campo 6: estado ( default A)

Insert Alumnos(Nombre, Direccion, Telefono, Nacimiento) values ('Juan Perez','Av. Arequipa 3025', GetDate())

Si no se especifican columnas es porque se van ingresar datos a todas las
columnas, en correspondencia a la estructura de la tabla.

Insert Alumnos values ('Juan Perez','Av. Arequipa 3025', GetDate(), 'B', 'Activo')

También, es posible insertar datos desde otra tabla, en caso la otra tabla cumpla con la estructura de la tabla destino.

Insert into Alumnos 
select * from Alumnos2;


UPDATE

Es posible modificar el contenido de las columnas de una tabla con la sentencia UPDATE.
Ejemplo:

update nombretable set campo1="dato1" where condicion;

Nota:
Si no se colocara la condición (Where ...), entonces todas las filas serían cambiadas a este teléfono.

También, es posible actualizar una tabla desde otras tablas. 
Por ejemplo:

update nombretable set campo1= (select campo from tabla2 where id=1) where condicion;


DELETE

Con esta sentencia se puede eliminar una o más filas de una tabla.

delete tabla where condicion;

Ejemplo:

delete alumnos where telefono is null;

También se puede borrar todo:

delete alumnos

También, es posible retirar filas de una tabla, según características de otra
tabla

delete nombretable where  campo1 in (select campo from tabla2 where id=1) ;

4. Conectividad con Java

La conectividad de la base de datos de Java (JDBC, Java Database Connectivity) es un marco de programación para los desarrolladores de Java que escriben los programas que tienen acceso a la información guardada en bases de datos.

JDBC, se utiliza comúnmente para conectar un programa del usuario con una base de datos por “detrás de la escena”, sin importar qué software de administración o manejo de base de datos se utilice para controlarlo. En Java existen 4 formas de usar JDBC para conexión con base de datos:

4.1 El puente JDBC-ODBC

Se usará, inicialmente, para facilitar la comunicación con el gestor SQL Server. Para esto se necesita del ODBC (Open Database Connectivity) de Microsoft, a través del cual se creará un DSN (Data Source Name) que permitirá crear una cadena de conexión de información sobre la base de datos.

4.2 Driver de Java parcialmente nativo

Esta forma está integrada por controladores que se comunican con el servidor de base de datos en el protocolo nativo del servidor. Por ejemplo, para el gestor DB2 se necesitaría un driver nativo de DB2 de IBM. Para Informix, se requerirá de un driver nativo de Informix de Unix.

4.3 Driver JDBC-Net de Java puro

En esta forma los drivers están hechos en Java puro; sin embargo, utilizan protocolos estándares, como por ejemplo HTTP con servidor de base de datos. El servidor traduce el protocolo de red. Para el caso de Windows, puede usar ODBC.

4.4 Driver de protocolo de Java puro

En esta última forma, conformada por drivers de java puro, la comunicación es a través de un protocolo específico para la marca de base de datos que se usa. Para mostrar un ejemplo del driver de protocolo de Java puro, se debe crear un proyecto en NetBeans y agregar al nodo Libraries, el driver de MySQL.



Luego, cree una clase Java con el siguiente contenido:



Si se tiene esta salida, entonces ya se tendrá conexión a la base de datos test de MySQL.


5. El paquete java.sql

Es una implementación de la API JDBC (usada para acceder a bases de datos SQL) donde se agrupa una serie de objetos como: Connection, Statement, PreparedStatement, ResulSet, ResultSetMetaData y otros.

5.1 Connection

Permite la conexión a la base de datos. Origina un canal entre una aplicación y la base de datos. Será siempre imprescindible en una aplicación que quiere acceder a una base de datos.}

Ejemplo de su uso:
Connection cn=DriverManager.getConnection("jdbc:mysql://localhost:3306/appweb", "root", "java");

5.2 Statement

Este objeto permitirá ejecutar una sentencia SQL para una base de datos, por ejemplo: select, insert, update y delete.

Ejemplo de su uso:
Sring sql = “SELECT * FROM provincias“;
Connection cn = db.getConnection();
Statement st = cn. createStatement();
ResultSet rs = st. executeQuery(sql);

5.3 PreparedStatement

Este objeto permitirá ejecutar una sentencia SQL para una base de datos usando parámetros de envío; por ejemplo: select, insert, update y delete.

Ejemplo de su uso:
Sring sql = “SELECT * FROM provincias LIMIT ?, ?“;
Connection cn = db.getConnection();
PreparedStatement ps = cn.prepareStatement(sql);
ps.setLong(1, 50);
ps.setLong(2, 50);
ResultSet rs = ps.executeQuery();

5.4 ResulSet

Si el objeto Statement ejecuta una sentencia Select del SQL, entonces, este devuelve un conjunto de resultados. Este conjunto de resultados es asignado y manipulado por un objeto ResulSet.

Ejemplo de su uso:
Sring sql = “SELECT * FROM provincias“;
Connection cn = db.getConnection();
Statement st = cn. createStatement();
ResultSet rs = st. executeQuery(sql);

Tabla de métodos Clase Connection-Resumen de método:
  • void: clearWarnings() Limpia todos los warnings reportados por el objeto Connection.
  • void close() Libera la base de datos de este objeto Connection y de los recursos consumidos por JDBC.
  • void commit() Ejecuta todos los cambios hechos desde el previo commit/rollback y libera a la base de datos de actuales bloqueos por este objeto Connection.
  • Statement createStatement() Crea un objeto Statement para enviar una sentencia SQL a la base de datos. 
  • Statement createStatement(int resultSetType, int resultSetConcurrency) Crea un objeto Statement, el cual generará objetos ResulSet con un tipo y concurrencia dada.
  • String getCatalog() Retorna el nombre del actual catálogo o base de datos.

Clase Connection-Resumen de métodos
  • DatabaseMetaData getMetaData() Recupera un objeto DatabaseMetaData, el cual contiene metadata de la base de datos con la que este objeto Connection ha establecido la conexión.
  • SQLWarning getWarnings() Recupera el primer warning reportado por la ejecución de este objeto Connection. 
  • boolean isClosed() Retorna true si el objeto Connection fue cerrado.
  • boolean isReadOnly() Retorna true si el objeto Connection es de modo read-only.
  • void rollback() Deja sin efecto todos los cambios hechos en la actual transacción y libera bloqueos en la base de datos retenidos por este objeto Connection.
  • void setCatalog(String catalog) Establece el nombre del catálogo o de la base de datos con la cual trabajará este objeto Connection. 
  • void setReadOnly(boolean readOnly) Pone esta conexión en modo read-only habilitando al driver para optimizaciones sobre la base de datos.

Clase Statement - Resumen de métodos

  • void addBatch(String sql) Adiciona un comando SQL dado a la actual lista de  comandos de este objeto Statement.
  • void cancel() Cancela este objeto Statement, si tanto el DBMS y el driver soportan abortar una sentencia SQL.
  • void clearBatch() Limpia el objeto Statement de la actual lista de comandos SQL.
  • void clearWarnings() Limpia todos los warnings reportados por este objeto Statement.
  • void close() Libera la base de datos de este objeto Statement y de los recursos consumidos por JDBC.
  • boolean execute(String sql) Ejecuta una sentencia SQL y retorna true, si retorna resultados.
  • int[] executeBatch() Ejecuta un lote de comandos SQL sobre una base de datos. Si la ejecución fue satisfactoria, retorna un arreglo de actualizaciones incluidas.
  • ResultSet executeQuery(String sql) Ejecuta una sentencia SELECT del SQL y retorna un único ResulSet.
  • int executeUpdate(String sql) Ejecuta una sentencia SQL como un INSERT, UPDATE o DELETE; o una sentencia SQL que no retorne nada como una sentencia DDL del SQL.
  • int getQueryTimeout() Recupera el número de segundos que el driver esperará para que un objeto Statement se ejecute.
  • ResultSet getResultSet() Recupera el actual resultado de un objeto ResultSet.
  • SQLWarning getWarnings() Recupera la primera warning reportada por la llamada a este objeto Statement.
  • void setCursorName(String name) Establece un nombre de cursor SQL, el cual puede ser usado por posteriores métodos del objeto Statement.
  • void setQueryTimeout(int seconds) Establece el número de segundos que el driver esperará para que un objeto Statement se ejecute.
Clase ResultSet - Resumen de métodos

  • boolean absolute(int row) Mueve el cursor al número de fila determinada en este objeto 
  • ResultSet. void cancelRowUpdates() Cancela los cambios realizados en la fila actual de este objeto ResultSet.
  • void close() Libera base de datos este objeto ResultSet JDBC y recursos inmediatamente en lugar de esperar a que esto suceda cuando se cierra automáticamente.
  • void deleteRow() Elimina la fila actual de este objeto ResultSet y de la base de datos subyacente.
  • int findColumn(String columnName) Asigna el nombre de la columna de resultados a su índice de la columna de resultados.
  • boolean first() Mueve el cursor a la primera fila de este objeto ResultSet.
  • boolean getBoolean(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un booleano en el lenguaje de programación Java.
  • boolean getBoolean(String columnName) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un booleano en el lenguaje de programación Java.
  • byte getByte(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un byte en el lenguaje de programación Java.
  • byte getByte(String columnName) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un byte en el lenguaje de programación Java.
  • String getCursorName() Recupera el Nombre del cursor SQL utilizada por Objeto ResultSet. 
  • Date getDate(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como objeto java.sql.Date en el lenguaje de programación Java.
  • Date getDate(String columnName) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como objeto java.sql.Date en el lenguaje de programación Java.
  • double getDouble(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un doble en el lenguaje de programación Java.

6. Metadatos

En ocasiones, se necesitan hacer aplicaciones en Java que trabajen contra una base de datos desconocida, de la que no se sabe qué tablas tiene, ni cuáles son sus columnas en cada tabla. Es, por ejemplo, el caso de aplicaciones que permiten visualizar el contenido (o incluso modificar el mismo) de una base de datos cualquiera, de una forma genérica. En este tipo de aplicaciones, a veces también se deja que el usuario escriba sus propias sentencias SELECT que el programa luego ejecuta y debe mostrar los resultados.

Las clases DataBaseMetaData y ResultSetMetaData permiten, respectivamente, analizar la estructura de una base de datos (qué tablas tiene, qué columnas cada tabla, de qué tipos, etc.) o de un ResultSet de una consulta, para averiguar cuántas columnas tiene dicho ResulSet, de qué columnas de base de datos proceden, de qué tipo son, etc.

ResultSetMetaData
Un objeto de esta clase tiene información meta sobre el conjunto de resultados, como por ejemplo: cuántas columnas tiene la consulta, los nombres de las columnas, los tipos de datos que guarda cada columna, cuántas filas, etc. Ejemplo de su uso:

Sring sql = “SELECT * FROM provincias“;
Connection cn = db.getConnection();
Statement st = cn. createStatement();
ResultSet rs = st. executeQuery(sql);
ResultSetMetaData rsmd = rs.getMetaData();
int ctascols = rsmd.getColumnCount();

7. Retorno y vista de consulta en el browser

Para mostrar una consulta en el browser, se debe considerar lo siguiente:

  • Los datos se muestran en una tabla HTML, por lo tanto, deben tener los estilos de tablas para que esta sea cebra, es decir, filas pares de un color y pares de otro color. 
  • El otro aspecto a considerar es la lectura de la base de datos para obtener la información a mostrar al cliente, por lo tanto, se debe considerar lo siguiente: 
  • El driver a usar, según la base de datos. 
  • La conexión de Java con el respectivo driver para acceder a la base de datos.
  • Tener una instrucción Statement para ejecutar una sentencia SQL. 
  • Tener una instrucción ResultSet para recoger la información leída de la base de datos. 
  • Tener una instrucción ResultSetMetaData para que informe de la metadata recogida por la consulta.
  • Finalmente, un objeto List que encapsule la información y se lo envíe al cliente.














sábado, 18 de mayo de 2019

Capítulo 9 Archivos y serialización

1. Conceptos sobre archivos

Los archivos también denominados ficheros son una colección de información (datos relacionados entre sí), localizada o almacenada como una unidad en alguna parte de la computadora. Los archivos son el conjunto organizado de informaciones del mismo tipo, que pueden utilizarse en un mismo tratamiento; como soporte material de estas informaciones.

Los archivos como colección de datos sirven para la entrada y salida a la computadora y son manejados con programas. Los archivos pueden ser contrastados con Arrays y registro; lo que resulta dinámico y, por esto, en un registro se deben especificar los campos, el número de elementos de un arrays (o arreglo), el número de caracteres en una cadena; por esto se denotan como "Estructuras estáticas".

En los archivos no se requiere de un tamaño predeterminado; esto significa que se pueden hacer archivos de datos más grandes o pequeños, según se necesiten. Cada archivo es referenciado por su identificador (su nombre). 

Las principales características de los archivos son:

  • Independencia de las informaciones respecto de los programas.
  • La información almacenada es permanente.
  • Un archivo puede ser accedido por distintos programas en distintos momentos.
  • Gran capacidad de almacenamiento.


Hay dos aspectos importantes en que difieren las organizaciones de archivos: la secuenciación de registros y el conjunto de operaciones para manipularlos. 

Por ello tenemos como modos básicos de acceso a:

  • Los archivos secuenciales o de texto
  • Los archivos de acceso aleatorio o binarios

2. Archivos secuenciales


La forma más sencilla de almacenar un conjunto de registros en un archivo es mediante la organización secuencial. En este tipo de archivos, los registros son escritos consecutivamente cuando el archivo es creado, por lo tanto, deben ser accesados de ese modo cuando se consultan. Es la forma básica de organizar un conjunto de registros, que forman un archivo, utilizando una organización
secuencial.

En un archivo organizado secuencialmente, lo registros quedan grabados consecutivamente cuando el archivo se utiliza como entrada. En la mayoría de los casos, los registros de un archivo secuencial quedan ordenados de acuerdo con el valor de algún campo de cada registro. Semejante archivo se dice que es un archivo ordenado; el campo, o los campos, cuyo valor se utiliza para determinar el ordenamiento es conocido como la llave del ordenamiento. Un archivo puede ordenarse ascendente o descendentemente con base en su llave de ordenamiento.

La característica más importante de esta técnica de organización de archivos es que solo permite el acceso secuencial, es decir, para acceder al registro k, se deben recorrer los k-1 registros anteriores.
Esta característica impide que el archivo secuencial se use en procesos en línea debido a que no se pueden consultar rápidamente los registros; sin embargo, muestra buen rendimiento en procesos en lote donde se aprovecha la capacidad para acceder al siguiente registro rápidamente. Otra ventaja de
esta organización de archivos radica en que la dirección del registro está implícita en el sistema; es decir, no se pierden registros por la desaparición de direcciones.

Estructura de la organización secuencial
Archivo secuencial es la forma más simple de almacenar y recuperar registros de un archivo. En un archivo secuencial, se almacenan los registros uno tras otro. El primer registro almacenado se coloca al principio del archivo. El segundo se almacena inmediatamente después (no existen posiciones sin
uso), el tercero después del segundo, etc. Este orden nunca cambia en la organización secuencial.
Una característica de los archivos secuenciales es que todos los registros se almacenan por posición: de primer registro, segundo registro etc. 

Ventajas y desventajas
Ventajas:
Los archivos secuenciales proveen la mejor utilización de espacio y son rápidos cuando los registros son accedidos secuencialmente. Los archivos con poca volatilidad, gran actividad y tamaño variable son altamente susceptibles de ser organizados secuencialmente. La ventaja más importante de la técnica de organización secuencial de archivos es la capacidad de acceso al "siguiente" registro rápidamente, mientras que el patrón de acceso a un archivo secuencial se conforme al ordenamiento de registros en el archivo, los tiempos de acceso serán muy buenos.

Sin embargo, si el patrón de acceso al programa no se conforma al patrón de ordenamiento de los registros, entonces la eficiencia del programa puede ser terrible. Otra ventaja de los archivos de organización secuencial es que son muy sencillos de usar y aplicar.

Desventajas:
El acceso a un registro es pobre, la localización de un determinado registro no se puede hacer individualmente no de manera rápida, y el acceso aleatorio es impráctico.
Además, en los archivos secuenciales la dirección de registro está implícita y están vulnerables a fallas del sistema.

Escritura de los archivos secuenciales
En estos archivos, la información solo puede leerse y escribirse empezando desde el principio del archivo.



Los archivos secuenciales tienen algunas características que hay que tener en cuenta:
1. La escritura de nuevos datos siempre se hace al final del archivo.
2. Para leer un dato concreto del archivo hay que avanzar siempre hasta donde se encuentre dicho dato. Si el dato requerido se encuentra antes del dato en que se está posicionado el archivo en un momento dado, será necesario regresar al comienzo del archivo y avanzar hasta el dato necesario.

Almacenamiento de archivos secuenciales
Los archivos secuenciales pueden almacenarse en dispositivos de acceso serial o directo. Con frecuencia los dispositivos de acceso serial son considerablemente menos caros que los dispositivos de acceso directo en un sistema de cómputo, pero de hecho, los dispositivos de almacenamiento de acceso directo en una computadora siempre proporcionan mayor capacidad de almacenamiento y acceso más rápido que los dispositivos de acceso serial. Para leer un archivo secuencial, el sistema siempre comienza al principio del archivo y lee un registro a la vez hasta llegar al registro deseado.

Por ejemplo, si ocurre que el registro particular es el décimo en un archivo, el sistema comienza en el primer registro y lee hacia delante un registro a la vez hasta llegar al décimo.

Actualización en archivos secuenciales
Un archivo maestro representa el punto estático de algún aspecto de alguna organización en un tiempo dado. Los cambios en la organización se reflejan en el archivo maestro, y para llevar a cabo la actualización del archivo maestro se tendrán que realizar los tipos de actualización: 
  • Insertar un nuevo registro
  • Borrar un registro
  • Modificar un registro
Al estar usando un archivo secuencial como archivo maestro, el realizar las operaciones de actualización se llevará con el auxilio de un archivo de transacciones, debido a que se realizará el proceso en lote para que sea más eficiente.

Creación de archivos secuenciales
La creación de un archivo secuencial se realiza agregando registros al final del archivo, no importa el medio de entrada de datos. El archivo secuencial puede ser almacenado en cintas o en discos magnéticos. Un archivo secuencial puede tener registros fijos o variables, la declaración del archivo y la definición del registro dependerá del lenguaje de programación que se vaya a usar.

Clasificación de los archivos secuenciales
Normalmente el uso de los archivos secuenciales se da en procesos en lote, donde se ha hecho notar que son eficientes cuando se llevan a cabo diversas operaciones sobre una gran cantidad de registros o de todo el archivo. Esta eficiencia se logra con una acción: la clasificación, proceso que no es exclusivo de los archivos secuenciales, pero sí necesaria para diversas operaciones.

La clasificación es el proceso de examinar los registros en un archivo y ponerlos en una secuencia ascendente o descendente basada en el valor de uno o más campos del registro. 

Recuperación de archivos secuenciales
Como se mencionó anteriormente la recuperación de información de los archivos se da como consulta (modo interactivo) o como la generación de reporte (modo lote). También se indicó la desventaja de acceder a un solo registro de un archivo secuencial.

Es ineficiente el uso de estos archivos para realizar la consulta de un registro, pero es óptimo su uso para la generación de un reporte de secuencia lógica en que se encuentra el archivo, el reporte podrá ser de varios tipos:
  • Selectivo
  • Resumen y sumarización
  • Clasificado por uno o más campos
Consideraciones de los archivos secuenciales el uso más común para los archivos secuenciales es para el procesamiento de lotes, tales como respaldo de datos, generación de reportes, transmisión física de datos, etc., archivo de nómina. 

Ventajas: los archivos secuenciales proveen la mejor utilización de espacio y son rápidos cuando los registros son accedidos secuencialmente. Los archivos con poca volatilidad, gran actividad y tamaño variables son altamente susceptibles de ser organizados secuencialmente.

Desventajas: el acceso a un registro es pobre, la localización de un determinado registro no se puede hacer individualmente ni rápidamente, el acceso aleatorio es impráctico. El archivo a causa de inserciones y supresiones, tiene que ser reescrito periódicamente. 

Uso de las Clases FileReader y FileWriter
Las clases FileReader y FileWriter permiten leer y escribir, respectivamente, en un fichero.

Lo primero que debemos hacer es importar estas clases y las que controlan las excepciones.

Después debemos crear un objeto de alguna de estas clases. Se pueden construir con un objeto File, FileDescriptor o un String.

Al crear un objeto de estas clases, deben estar dentro de un try-catch, para poder manejar las excepciones.

Cuando creamos un objeto, abrimos un stream entre nuestro programa y el  exterior, cuando debemos de usarlo debemos cerrar el stream con el método close().



Ejemplo de creación de los objetos
FileWriter fw=new FileWriter("D:\\fichero1.txt");
FileReader fr=new FileReader("D:\\fichero1.txt");

Al instanciar las clases escribimos la ruta del fichero. En el caso de FileWriter si la ruta del fichero que no existe este la crea, para FileReader sí debe existir el fichero, ya que si no existe lanzará una excepción. Usamos doble barra (\\) porque es un carácter de escape, para poner \.

Ejemplo:
Escribir y leer texto en un fichero.
Para escribir, usaremos el método write de FileWriter, este método puede usar como parámetro un String con lo que queremos escribir o un número que se corresponderá un carácter de la tabla ASCII.

Para leer, usaremos el método read de FileReader, este método no tiene parámetros pero devuelve un número que si le hacemos un casting a char este será legible por nosotros. Esto lo podemos mostrar por pantalla o incluso pasarlo a otro fichero (crear otro objeto). Cuando se termina el fichero, el método read devuelve -1.

import java.io.*;
//Importamos todas las clases de java.io
public class FicheroTextoApp {
public static void main(String[] args) {
try{
//Abro stream, crea el fichero si no existe
FileWriter fw=new FileWriter("D:\\fichero1.txt");
//Escribimos en el fichero un String y un caracter 97 (a)
fw.write("Esto es una prueb");
fw.write(97);
//Cierro el stream
fw.close();
//Abro el stream, el fichero debe existir
FileReader fr=new FileReader("D:\\fichero1.txt");
//Leemos el fichero y lo mostramos por pantalla
int valor=fr.read();
while(valor!=-1){
System.out.print((char)valor);
valor=fr.read();
}
//Cerramos el stream
fr.close();
}catch(IOException e){
System.out.println("Error E/S: "+e);
}
}
}

La idea es abrir un fichero, escribir o leer y cerrar el fichero. No es necesario que hagamos nada para mover el puntero del fichero, cuando leemos un carácter automáticamente el puntero se mueve (acceso secuencial).

3. Archivos de acceso aleatorio

Permiten situarse en cualquier punto del archivo para actualizar ciertos valores.
La clase Java RandomAccesFile del paquete java.io implementa un archivo de acceso aleatorio, puede ser utilizado tanto para lectura como escritura de bytes.

Todo objeto, instancia de RandomAccesFile , soporta el concepto de puntero que indica la posición actual dentro del archivo. Es un entero que empiza en 0 y mide en bytes.

Los archivos de acceso secuencial son muy útiles, pero a veces son necesarios archivos de acceso aleatorio que permiten acceder a sus contenidos en forma aleatoria o no secuencial.

La clase Java RandomAccessFile del paquete java.io implementa un archivo de acceso aleatorio, puede ser utilizado tanto como para lectura como escritura de bytes.

Todo objeto, instancia de RamdomAccessFile, soporta el concepto de puntero que indica la posición actual dentro del archivo. Es un entero que empieza en 0 y se mide en bytes.

RamdomAccessFile provee comandos para operar sobre el puntero.

Los constructores de la clase son:
RamdomAccessFile(String path, String modo);
RamdomAccessFile(File objetoFile, String modo);

Lanzan una excepción FileNotFoundException.
El argumento modo indica el modo de acceso en el que se abre el fichero.

Los valores permitidos para este parámetro son:
  • "r": Abre el fichero en modo solo lectura. El fichero debe existir.  Una operación de escritura en este fichero lanzará una excepción IOException.
  • "rw": Abre el fichero en modo lectura y escritura. Si el fichero no existe se crea.

Ejemplo: abrir un fichero aleatorio para lectura
Se abre el fichero clientes.dat para lectura usando el primer constructor.
RandomAccessFile fichero = new
RandomAccessFile("/ficheros/clientes.dat", "r");

Ejemplo: abrir un fichero aleatorio para lectura/escritura
Se abre el fichero personas.dat para lectura/escritura usando el segundo
constructor. Si el fichero no existe se crea.
File f = new File ("/ficheros/personas.dat");
RandomAccessFile fichero = new RandomAccessFile(f, "rw");

Acceso a datos en ficheros aleatorios
Para acceder de forma aleatoria a los datos contenidos en el fichero, la clase RandomAccessFile dispone de varios métodos. Entre ellos:

long getFilePointer();
Devuelve la posición actual del puntero del fichero. Indica la posición (en bytes)
donde se va a leer o escribir.

long length();
Devuelve la longitud del fichero en bytes.

void seek(long pos);
Coloca el puntero del fichero en una posición determinada. La posición se da como un desplazamiento en bytes desde el comienzo del fichero. La posición 0 indica el principio del fichero. La posición length() indica el final del fichero.

Además, dispone de métodos de lectura/escritura:

public int read()
Devuelve el byte leído en la posición marcada por el puntero. Devuelve -1 si alcanza el final del fichero. Se debe utilizar este método para leer los caracteres de un fichero de texto.

public final String readLine()
Devuelve la cadena de caracteres que se lee, desde la posición marcada por el puntero, hasta el siguiente salto de línea que se encuentre. 

public xxx readXxx()
Hay un método read para cada tipo de dato básico: readChar, readInt, readDouble, readBoolean, etc.

public void write(int b)
Escribe en el fichero el byte indicado por parámetro. Se debe utilizar este método para escribir caracteres en un fichero de texto.

public final void writeBytes(String s)
Escribe en el fichero la cadena de caracteres indicada por parámetro.

public final void writeXxx(argumento)
También existe un método Write para cada tipo de dato básico:
writeChar, writeInt, writeDouble, writeBoolean, etc.


4. La Interfaz serializable


La serialización es la transformación de un objeto en una secuencia de bytes que pueden ser posteriormente leídos para reconstruir el objeto original.

El objeto serializado puede guardarse en un fichero o puede enviarse por red para reconstruirlo en otro lugar. Puede crearse en un sistema Windows y enviarlo, por ejemplo, a otro sistema que utilice Linux.

Guardar objetos de forma que existan cuando la aplicación haya terminado se conoce como persistencia. Para poder transformar el objeto en una secuencia de bytes, el objeto debe ser serializable.

Un objeto es serializable si su clase implementa la interface Serializable. La interface Serializable se encuentra en el paquete java.io. Es una interface vacía. No contiene ningún método.

Public interface Serializable {
}

Sirve para indicar que los objetos de la clase que lo implementa se pueden serializar. Solo es necesario que una clase la implemente para que la máquina virtual pueda serializar los objetos.

Si un objeto contiene atributos que son referencias a otros objetos estos a su vez deben ser serializables.


Todos los tipos básicos Java son serializables, así como los arrays y los Strings.




5. Logging: niveles, formatos y configuraciones

Los objetos LOG son usados para enviar mensajes sobre las excepciones, advertencias o información sobre ocurrencias del sistema. En Java la funcionalidad para operaciones LOG esta implementada en el paquete java.util.logging.

Los mensajes log pueden ser enviados a diversos dispositivos, como a consola, archivos log, base de datos, y otros. 

Niveles
Existen varios niveles de log, como muestra la siguiente lista:

SEVERE: Describe eventos que son de mucha importancia para el sistema a fin de que este no tenga un colapso severo.
WARNING: Describe mensajes de peligro o advertencia de sucesos de más bajo nivel que SEVERE.
INFO: Describe mensaje solo de información.
FINE: Describe sucesos sobre fallas menores (recuperables).


Ejemplo de uso de logging.

Formatos
Los formatos de los mensajes log están expresados a través de los parámetros que se desean incluir en el mensaje, como por ejemplo:

Object[] parametros = new Object[]{5, "hola", 8f};
LOGGER.log(Level.INFO,
"\np 0: {0}"
+ "\np 1: {1}"
+ "\np 2: {2}"
+ "\np 1: {1} (2da. vez)", parametros);

El arreglo parámetros de objetos contiene los datos a insertar en el mensaje:
"\np 0: {0}"
+ "\np 1: {1}"
+ "\np 2: {2}"
+ "\np 1: {1} (2da. vez)", parametros);

Entonces, el primer valor del arreglo se inserta en {0}, el segundo en {1} y así sucesivamente. También se puede repetir un dato como muestra el ejemplo al repetir el segundo valor, de la siguiente forma:

"\np 1: {1} (2da. vez)"

Configuraciones
Por defecto los mensajes log se pueden mostrar en consola, pero mediante un archivo properties (log. properties) se puede especificar que estos se almacenen en el archivo en el disco duro. Observe el contenido de un archivo de configuración:











Capítulo 8 Hilos y Concurrencia

1. Hilos

Al arrancar un programa Java, este se ejecuta por defecto sobre un único hilo de ejecución, es decir, por ejemplo, cuando se programa un evento listener en un botón cuya ejecución resultase muy pesada, tardando un tiempo prolongado en procesarse, provocaba que la ventana quedase bloqueada
no pudiendo recibir más eventos hasta que un evento listener se terminase de ejecutar. Para estos casos, es necesario ejecutar el evento en otro hilo en paralelo al hilo principal.

Java posee mecanismos para ejecutar ciertas clases abriendo un nuevo hilo (thread). A la hora de crear hilos, tenemos que tener presente que en el sistema operativo solo existirá un único proceso de ejecución (el programa java.exe que lanzó la aplicación en el caso de SO Windows), por lo tanto,
todos los threads abiertos sobre este proceso compartirán ciertos recursos asignados por el sistema operativo, por ejemplo, la cantidad de memoria atribuida al proceso tendrá que ser administrada para todos los hilos. Para tener claro el concepto de thread, podemos imaginar la propiedad de
auto salvado de la mayoría de procesadores de texto. Esta funcionalidad se ejecuta en segundo plano según se va introduciendo texto en el documento, es decir la operación de auto salvado no bloquea al usuario, permitiéndoles que siga escribiendo mientras el archivo se está guardando.

2. Clase Thread e Interfaz Runnable

Un thread o un hilo de ejecución en Java, generalmente se abrirá a través de la clase java.lang.thread, que a su vez implementa la interfaz java.lang.runnable.

2.1 ¿Qué es un Thread?


Los principales métodos del objeto Thread son:

La clase Thread también dispone de una serie de métodos estáticos:



Entre los métodos de las dos tablas anteriores, no se ha hecho referencia a dos funciones de vital importancia en el manejo de hilos. Estos son los métodos wait() y notify(), que derivan de la superclase Object. El método wait dejará el hilo actual en un estado de espera, hasta que se invoque al método notify que volverá a activar el hilo.

2.2 Ciclo de vida de un thread


Como vemos en la figura anterior un thread puede tener los
siguientes estados:
  • Ready: El thread está listo para ser ejecutado.
  • Running: El thread está siendo ejecutado.
  • Waiting: El thread está esperando a que ocurra un evento para continuar con su ejecución.
  • Sleeping: El thread se encuentra detenido.
  • Blocked: El thread está esperando a que se liberen los objetos necesarios para poder continuar.
  • Dead: Finalización de un thread.

2.3 Creando un thread



Existen dos formas básicas para implementar un thread, tal y como se refleja en la figura.
La primera técnica que veremos para crear un nuevo hilo de ejecución será extendiendo la propia clase Thread.
Clase que extiende de Thread.

En el ejemplo anterior hemos extendido la clase Thread sobrescribiendo el método run que será el que se ejecute en el nuevo hilo cuando se invoque al método start de la clase, tal y como lo hace la main del listado. En ambas clases hemos llamado al método sleep, para que los hilos se queden dormidos durante un corto período de tiempo y permitirnos ver mejor como las dos clases se ejecutan en threads distintos, de ahí la razón que al invocar a la clase main, podamos observar cómo se entremezclan en la consola de salida, los System.out de la clase Hilos y ThreadExample, esto quiere decir que se están ejecutando en paralelo.

La otra opción que tenemos a la hora de crear threads es implementar la interfaz Runnable, que al igual que en el caso anterior, su método principal tendrá el nombre run (). Para ejecutar la implementación de Runnable como un nuevo hilo, pasaremos una instancia de la misma al constructor de la clase Thread.

Por ejemplo:
new Thread (new
MiClaseQueImplemetaRunnable()).start().

Así pues, podemos cambiar los listados para construir el thread
implementando a la interfaz Runnable.
Clase que implementa Runnable.

Clase que se ejecuta en un thread principal y abre uno nuevo.

2.4 Sincronización de métodos

Cuando trabajamos con múltiples hilos, tal vez necesitamos que un método no pueda ser ejecutado al mismo tiempo por varios threads, por ejemplo, tal vez tengamos una función que escribe en un fichero, resultando imposible que varios hilos intenten a la vez, escribir en él. Para estos casos existe el modificador synchronized, que utilizaremos en la definición del método que queremos que sea sincronizado entre distintos hilos. Si un thread intenta acceder a un método sincronizado que está siendo ejecutado por otro hilo, se quedará bloqueado hasta que el primer hilo termine de ejecutarlo. 

Esto puede ser peligroso ya que puede ocurrir que un método sincronizado invoque de forma incorrecta a otro que también lo es, dejando los hilos bloqueados. Para ver cómo funcionan los métodos sincronizados utilizaremos los ejemplos anteriores, pero en vez de escribir trazas a través de la instrucción System.out, lo haremos a través de un método de una nueva clase EscribeTrazas:

Clase EscribeTrazas
La clase anterior dispone de dos métodos estáticos que escriben 100 veces el texto pasado como parámetro. La única diferencia entre ellos es que uno es sincronizado. Comenzaremos primero modificando  a clase Hilos y RunnableExample para invocar al método no sincronizado.


Clase RunnableExample invocando a EscribeTrazas.


Creando un nuevo hilo e invocando a EscribeTrazas.


 Si ejecutamos el ejemplo anterior, obtendremos las trazas entremezcladas, ya que tanto el hilo de la main y el de RunnableExample, escriben a través de un método no sincronizado.

Si cambiamos y utilizamos el método escribeSyn en lugar de escribe, la salida de las trazas cambiará, escribiendo las 100 líneas del primer hilo que lo ha ejecutado y a continuación las 100 del otro.

Finalmente, añadir que la interfaz Runnable es considerada como una interfaz funcional, por lo tanto se pueden aplicar expresiones Lambda. Por ejemplo, podríamos simplificar los programas de la  siguiente manera.

new Thread (()-> EscribeTrazas.escribeSyn("Ejecutando
Thread")).start();
        EscribeTrazas.escribeSyn("Main Thread:");





sábado, 4 de mayo de 2019

Capítulo 7 - Arreglos y colecciones

1. Conceptos y creación de arreglos


Un arreglo o array es un medio de guardar un conjunto de objetos de la misma clase. Se accede a cada elemento individual del array mediante un número entero denominado índice. Cero (0) es el índice del primer elemento y N-1 es el índice del último elemento, siendo N, la dimensión del array.

Los array tienen el atributo length que proporciona la cantidad de elementos que se pueden asignar en el array.

  • Declaración y creación de un array

Para declarar un array, se escribe lo siguiente:
tipo_de_dato[] nombre_del_array;
Para declarar un array de enteros, se escribe lo siguiente:
int[] numeros;
Para crear un array de 4 números enteros, se escribe lo siguiente:
numeros = new int[4];
La declaración y la creación del array, se puede hacer en una misma línea.
int[] numeros = new int[4];
Inicialización y uso de los elementos del array
Para inicializar el array de 4 enteros, se escribe lo siguiente:
numeros[0]=2;
numeros[1]=-4;
numeros[2]=15;
numeros[3]=-25;
Se puede inicializar en un bucle for como resultado de alguna operación. Por ejemplo:
for(int i=0; i<4; i++){
numeros[i]=i*i+4;
}
No se necesita recordar el número de elementos del array, su atributo length proporciona la dimensión del array. Así, se escribirán de forma equivalente, las siguientes líneas:
for(int i=0; i<numeros.length; i++){
numeros[i]=i*i+4;
}
Los arrays se pueden declarar, crear e inicializar en una misma línea, del
siguiente modo:
int[] numeros = {2, -4, 15, -25};String[] nombres = {"Juan", "José", "Miguel", "Antonio"};
Para imprimir a los elementos de array nombres, se escribe lo siguiente:
for(int i=0; i<nombres.length; i++){System.out.println(nombres[i]);}
Java verifica que el índice no sea mayor o igual que la dimensión del array, lo
que facilita mucho el trabajo al programador.
Para crear un array de tres objetos de la clase Rectángulo, se deberá escribir
lo siguiente:
  • Declarar

Rectangulo[] rectangulos;
  • Crear el array

rectangulos = new Rectangulo[3];
  • Inicializar los elementos del array

rectangulos[0]=new Rectangulo(10, 20, 30, 40);rectangulos[1]=new Rectangulo(30, 40);rectangulos[2]=new Rectangulo(50, 80);
O bien, se puede escribir lo anterior en una sola sentencia.
Rectangulo[] rectangulos={new Rectangulo(10, 20, 30, 40),new Rectangulo(30, 40),new Rectangulo(50, 80)};
  • Usar el array

Para calcular y mostrar el área de los rectángulos, se escribe lo siguiente:
for(int i=0; i<rectangulos.length; i++){System.out.println(rectangulos[i].calcularArea());}
  • Arrays multidimensionales

Una matriz bidimensional puede tener varias filas y en cada fila, no tiene necesariamente, que a ver el mismo número de elementos o columnas. Por ejemplo, se puede declarar e inicializar la siguiente matriz bidimensional:
double[][] matriz={{1,2,3,4}, {5,6}, {7,8,9,10,11,12}, {13}};
    • La primera fila tiene cuatro elementos {1,2,3,4}.
    • La segunda fila tiene dos elementos {5,6}.
    • La tercera fila tiene seis elementos {7,8,9,10,11,12}.
    • La cuarta fila tiene un elemento {13}.

Para mostrar los elementos de este array bidimensional, se escribe el siguiente código:
for (int i=0; i < matriz.length; i++) {for (int j=0; j < matriz[i].length; j++) {System.out.print(matriz[i][j]+"\t");}System.out.println("");}
Como se puede apreciar, matriz.length proporciona el número de filas (cuatro), mientras que matriz[i].length proporciona el número de elementos en cada fila.

Se muestran los elementos de una fila, separados por un tabulador, usando la función print. Una vez completada una fila se pasa a la siguiente mediante println.

1.1 Clases y métodos para manejar arreglos

El convertir colecciones en arrays, puede ser una tarea sencilla, pero muchas personas no saben cómo hacerlo y terminan en una instrucción for convirtiendo dato por dato. A continuación, se muestran las formas más simples de dicha conversión.
  • De una lista para un arreglo

List<String> list = new ArrayList<String>();list.add("India");list.add("Switzerland");list.add("Italy");list.add("France");Object[] countries = list.toArray(); // forma 1String[] countries = (String[]) list.toArray(new String[0]); // forma 2
  • De un arreglo para una lista

String[] countries = {"India", "Switzerland", "Italy", "France"};List list = Arrays.asList(countries);
  • De un objeto map a un arreglo

Map<String, String> map = new HashMap<String, String>();map.put("cliente", "Julio Torres");map.put("correo", "jtorres@yahoo.es");map.put("telefono", "345-4565");Object[] key = map.keySet().toArray();Object[] value = map.values().toArray();
  • De un objeto set a un arreglo

Set<String> set = new HashSet<String>();set.add("Naranja");set.add("Mandarina");set.add("Naranja"); // no permite repetidoset.add("Pera");Object[] fruta = set.toArray();
  • De una cadena a un arreglo de caracteres

String msg = "Cibertec - DAT";char[] letra = msg.toCharArray();
  • De arreglo de caracteres a una cadena

char[] charArrays = new char[]{'1', '2', '3', 'A', 'B', 'C'};String s1 = new String(charArrays); // forma 1String s2 = String.valueOf(charArrays); // forma 1

2. Java Collection Framework

Java Collection Framework o “estructura de colecciones de Java” (en adelante JCF), se trata de un conjunto de clases e interfaces que mejoran notablemente las capacidades del lenguaje respecto a estructuras de datos. Además, constituyen un excelente ejemplo de aplicación de los conceptos propios de la programación orientada a objetos. El corazón de las interfaces de colección encapsula diferentes tipos de interfaces que se muestran en la siguiente figura:



Para este caso, se desarrollarán los siguientes:
  • List y ArrayList
  • Map y HashMap
  • Set y HashSet

2.1 List y ArrayList

La clase ArrayList apoya a los vectores dinámicos que pueden crecer según sea necesario. En Java, los arrays son de una longitud fija. Después de que los arrays se crean, no pueden crecer o disminuir, lo que significa que debe saber de antemano cuántos elementos de una matriz necesita, pero, a veces, puede que no sepa hasta el tiempo de ejecución. Para manejar esta situación, JCF define la clase ArrayList que es de longitud variable, es decir, dinámicamente puede aumentar o disminuir de tamaño.

Operaciones con List y ArrayList
  • Definición de un ArrayList

List<String> list = new ArrayList< >();
    • Agregar elementos

list.add("Andrea");
list.add("Andrea");list.add("Ana");list.add("Julio");list.add("Jorge");list.add("Mary");
Obtención de un elemento
String s = list.get(0); // devuelve el primero (Andrea)

Cantidad de elementos
int ctos = list.size();

Sublista
List list2 = list.subList(1, 3); // retorna. Ana y Julio

Eliminación de un elemento
list.remove(1); // elimina el segundo

Borrar una lista
list.clear();

Verificación si la lista está vacía
boolean flag = list.isEmpty(); // true si está vacía

Retorno del contenido como un arreglo de objetos
Object[] arreglo = list.toArray();
    • Recorrer list con for (cada elemento de list, se asigna en nombre):

          for (String nombre : list) {                   System.out.println(nombre);          }

2.2 Map y HashMap

La clase hashMap permite seleccionar objetos contenidos en él, a través de un índice numérico.
Pero, ¿qué es lo que sucede cuando se realizan selecciones utilizando otros criterios? Se piensa en un mapa. Conceptualmente, todo parece ser un vector, pero en lugar de acceder a los objetos a través de un número, en realidad se utiliza otro objeto, lo cual lleva a utilizar claves.

Operaciones con Map y HashMap
Definición de una HashMap
Map<String, String> map = new HashMap<String, String>();

Agregar elementos
map.put("Andrea", "962-345-678");
map.put("Ana", "876-234-655");
map.put("Julio", "345-4565");
map.put("Jorge", "997-234-446");
map.put("Mary", "987-345-456");

Obtener un elemento
map.get("Mary"); // devuelve 987-345-456

Cantidad de elementos
int ctos = map.size();

Verificación si existe key
boolean existe = map.containsKey("Julio"); // true

Verificación si existe value
boolean existe = map.containsValue("345-4565"); // true

Eliminación de un elemento
map.remove("Ana"); // elimina Ana y 876-234-655

Limpiar un mapa
map.clear();

Verificación si la lista está vacía
boolean flag = map.isEmpty(); // true si está vacía

Retorno de sus values como una colección de cadenas
Collection<String> c = map.values();
for(String s : c) {
System.out.println(s);

El siguiente fragmento de código muestra como recorrer un objeto Map para obtener su key y value:
Map<Integer, String> datos = new HashMap<Integer, String>();
datos.put(1, "dato1");
datos.put(2, "dato2");
datos.put(3, "dato3");
for (Map.Entry<Integer, String> entry : datos.entrySet()) {
System.out.println("clave=" + entry.getKey()
+ ", valor=" + entry.getValue());
}

2.3 Set y HashSet

La clase HashSet no permite elementos repetidos. La interfaz Set contiene solo métodos heredados de Collection y aumenta la restricción de elementos duplicados.

Esta interfaz tiene un comportamiento más específico en operaciones con equals y hashCode permitiendo a instancias de esta interfaz, ser comparadas significativamente, aún si el tipo de implementación difiere. Dos instancias de Set son iguales si ellas contienen los mismos elementos. Método HashCode, este método viene a complementar al método equals y sirve para comparar objetos de una forma más rápida en estructuras Hash puesto que únicamente nos devuelve un número entero. Cuando Java compara dos objetos en estructuras de tipo hash (HashMap, HashSet etc) primero invoca al método hashcode y luego el equals. Si los métodos hashcode de cada objeto devuelven diferente, Java no seguirá comparando y considerará a los objetos distintos.

En el caso en el que ambos objetos compartan el mismo hashcode Java invocará al método equals() y revisará a detalle si se cumple la igualdad. De esta forma las búsquedas quedan simplificadas en estructuras hash. Manejando colecciones

Muchas veces se nos olvida que la invocación a los métodos equals y hashcode forma parte intrínseca del framework de colecciones. Por ejemplo, si construimos dos objetos de tipo Persona y los añadimos a unHashSet, podemos comprobar que la persona existe utilizando el método contains dentro del conjunto.



Método equals y sobreescritura de metodos equals y hashcode El método equals es el más sencillo de entender ya que comprueba si los dos objetos son del mismo tipo y si su nombre coincide. Vamos a crear una implementación por defecto para la clase Persona que realice un override sobre los métodos equals y hashcode.

2.3 Operaciones con Set y HashSet

Definición de una HashSet
Set<String> set = new HashSet<String>();

Añadir elementos
set.add("Andrea");
set.add("Ana");
set.add("Julio");
set.add("Julio"); // NO es aceptado (lo ignora)
set.add("Mary");

Obtención de la cantidad de elementos
int ctos = set.size();

Verificación si existe Julio
boolean existe = set.contains("Julio"); // true

Eliminación de un elemento
map.remove("Julio "); // elimina Julio

Borrar un set
set.clear();

Verificación si el conjunto está vacío
boolean flag = set.isEmpty(); // true si está vacío

Retorno de su contenido como un arreglo de objetos
Object[] a = set.toArray();
for(String s : a) {
System.out.println(s);
}

Suponiendo que s1 y s2 son Sets y las operaciones sobre éstos:
- s1.containsAll(s2) retorna true, si s2 es un sub conjunto de s1, es
decir si s1 contiene todos los elementos de s2.
- s1.addAll(s2) transforma a s1 en la unión de s1 más s2.
- s1.retainAll(s2) transforma a s1 en la intersección de s1 y s2. La
diferencia entre s1 y s2.

2.4 Iterator

Iterator nos permite recorrer los objetos colección del JCF, básicamente, un iterator es un apuntador a una colección, que recorre uno a uno los elementos de una colección, usa su método next() dentro de un While para recorrer los elementos de la colección. Por ejemplo, se puede recorrer una lista con un objeto iterator de la siguiente manera:



Salida en consola.



También, se puede aplicar Iterator a un Set del siguiente modo: