Professional Documents
Culture Documents
Hasta ahora sólo hemos tenido que trabajar con algunas variables en cada uno de los
programas que hemos realizado. Sin embargo, en más de una ocasión tendremos que
manipular conjuntos más grandes de valores. Por ejemplo, para calcular la
temperatura media del mes de agosto necesitaremos conocer los 31 valores
correspondientes a la temperatura media de cada día. En este caso, podríamos utilizar
una variable para introducir los 31 valores, uno cada vez, y acumular la suma en otra
variable. Pero ¿qué ocurrirá con los valores que vayamos introduciendo? Que cuando
tecleemos el segundo valor, el primero se perderá; cuando tecleemos el tercero, el
segundo se perderá, y así sucesivamente. Cuando hayamos introducido todos los
valores podremos calcular la media, pero las temperaturas correspondientes a cada
día se habrán perdido. ¿Qué podríamos hacer para almacenar todos esos valores?
Pues, podríamos utilizar 31 variables diferentes; pero ¿qué pasaría si fueran 100 o más
valores los que tuviéramos que registrar? Además de ser muy laborioso el definir cada
una de las variables, el código se vería enormemente incrementado.
En este tema, aprenderemos a registrar conjuntos de valores, todos del mismo tipo, en
unas estructuras de datos llamadas matrices. Asimismo, aprenderá a registrar cadenas
de caracteres, que no son más que conjuntos de caracteres, o bien, matrices de
caracteres.
Si las matrices son la forma de registrar conjuntos de valores, todos del mismo tipo
(int, float, double, char, String, etc.), ¿qué haremos para almacenar un conjunto
de valores relacionados entre sí, pero de diferentes tipos? Por ejemplo, almacenar los
datos relativos a una persona como su nombre, dirección, teléfono, etc. Ya hemos
visto que esto se hace definiendo una clase; en este caso, podría ser la clase de objetos
persona. Posteriormente podremos crear también matrices de objetos, cuestión que
aprenderemos más adelante.
1. INTRODUCCIÓN A LAS MATRICES
Una matriz es una estructura homogénea, compuesta por varios elementos, todos del
mismo tipo y almacenados consecutivamente en memoria. Cada elemento puede ser
accedido directamente por el nombre de la variable matriz seguido de uno o más
subíndices encerrados entre corchetes.
𝑣 = [𝑎0 , 𝑎1 , 𝑎2 , … , 𝑎𝑖 , … , 𝑎𝑛 ]
Por ejemplo, supongamos que tenemos una matriz unidimensional de enteros llamada
m, la cual contiene 10 elementos. Estos elementos se identificarán de la siguiente
forma:
Observe que los subíndices son enteros consecutivos, y que el primer subíndice vale 0.
Un subíndice puede ser cualquier expresión entera positiva. Asimismo, una matriz de
dos dimensiones se representa mediante una variable con dos subíndices (filas,
columnas); una matriz de tres dimensiones se representa mediante una variable con
tres subíndices etc. El número máximo de dimensiones o el número máximo de
elementos para una matriz, dentro de los límites establecidos por el compilador,
depende de la memoria disponible.
Igual que sucede con otras variables, antes de utilizar una matriz hay que declararla. La
declaración de una matriz especifica el nombre de la matriz y el tipo de elementos de
la misma. Para crear y utilizar una matriz hay que realizar tres operaciones: declararla,
crearla e iniciarla.
tipo[] nombre;
tipo nombre[];
donde tipo indica el tipo de los elementos de la matriz, que pueden ser de cualquier
tipo primitivo o referenciado; y nombre es un identificador que nombra a la matriz. Los
corchetes modifican la definición normal del identificador para que sea interpretado
por el compilador como una matriz. Veamos algunos ejemplos. Las siguientes líneas de
código son ejemplos de declaraciones de matrices:
int[] m;
float temperatura[];
COrdenador[] ordenador; // COrdenador es una clase de objetos
La primera línea declara una matriz de elementos de tipo int; la segunda, una matriz
de elementos de tipo float; y la tercera una matriz de objetos COrdenador. Las
declaraciones no especifican el tamaño de la matriz. El tamaño será especificado
cuando se cree la matriz, operación que se hará durante la ejecución del programa.
Las siguientes líneas de código crean las matrices declaradas en el ejemplo anterior:
m = new int[10];
temperatura = new float[31];
ordenador = new COrdenador[25];
Es bastante común declarar y crear la matriz en una misma línea. Esto puede hacerse
así:
Cuando se crea una matriz, el tamaño de la misma puede ser también especificado
durante la ejecución a través de una variable a la que se asignará como valor el
número de elementos requeridos. Por ejemplo, la última línea de código del ejemplo
siguiente crea una matriz con el número de elementos especificados por la variable
nElementos:
int nElementos;
System.out.print(“Numero de elementos de la matriz: ”);
nElementos = (int) System.in.read();
int[] m = new int[nElementos];
Una matriz es un objeto; por lo tanto, cuando es creada, sus elementos son
automáticamente iniciados, igual que sucedía con las variables miembro de una clase.
Si la matriz es numérica, sus elementos son iniciados a 0 y si no es numérica, a un
valor análogo al 0; por ejemplo, los caracteres son iniciados al valor ‘\u0000’, un
elemento booleano a false y las referencias a objetos, a null. Si deseamos iniciar una
matriz con otros valores diferentes a los predeterminados, podemos hacerlo de la
siguiente forma:
El ejemplo anterior crea una matriz temperatura de tipo float con tantos elementos
como valores se hayan especificado entre llaves.
Observamos que para referenciar un elemento de una matriz se puede emplear como
subíndice una constante, una variable o una expresión de tipo entero. El subíndice
especifica la posición del elemento dentro de la matriz. La primera posición es la 0. Si
se intenta acceder a un elemento con un subíndice menor que cero o mayor que el
número de elementos de la matriz menos uno, Java lanzará una excepción de tipo
ArrayIndexOutOfBoundsException, indicando que el subíndice esta fuera de los
límites establecidos cuando se creó la matriz.
int nElementos;
nElementos = (int) System.in.read();
int[] m = new int[nElementos]; // Crear la matriz m
int i = 0; // Subíndice
El tamaño de una matriz de una dimensión viene dado por su atributo length.
2.2. ArrayList
donde tipo indica el tipo de los elementos de la lista, el cual puede ser cualquier tipo
referenciado, y nombre es un identificador que nombra a la lista. El número de
elementos de la lista será inicialmente cero.
El método hasNext devuelve true si el Iterator tiene más elementos, next retoma el
siguiente elemento y remove elimina el último elemento que fue devuelto por next. El
siguiente ejemplo elimina los múltiplos de dos de la colección m y muestra el resto de
los elementos:
int n;
Iterator it = m. iterator();
while (it.hasNext())
{
n = (lnteger) it.next():
if (n % 2 == 0)
it.remove();
else
System.out.print(n + “ “);
}
Para conocer el tamaño de una colección hay que invocar a su método size.
Para saber si una colección está vacía, utilizar el método isEmpty. Este método
devuelve true si una colección está vacía y false en caso contrario.
Una lista puede ser copiada en otra invocando a su método clone. Por ejemplo:
Los datos leídos serán almacenados en la lista notas, proceso que finalizará cuando se
introduzca como nota media un valor -1. Finalmente se suman todas las notas y se
visualiza la nota media. La suma se almacenará en la variable suma. Una variable
utilizada de esta forma recibe el nombre de acumulador. Es importante que observe
que inicialmente su valor es cero.
// Entrada de datos
int i = 0;
System.out.println(“Introducir notas medias. Finalizar con -1.”);
System.out.printf("Alumno número %3d, nota media: “, ++i);
float nota = (float) System.in.read();
while (nota != -1)
{
notas.add(nota); // añadir un elemento
System.out.printf("Alumno número %3d, nota media: ", ++i);
nota = (float) System.in.read();
}
// Escribir resultados
System.out.printf(“\n\nNota media del curso: %5.2f\n”,
suma / nalumnos);
}
}
En una matriz asociativa el acceso a los elementos se hace por valor en lugar de por
posición (por ejemplo, una matriz diasMes[13] que almacene en el elemento de
índice 1 los días del mes 1, en el de índice 2 los días del mes 2 y así sucesivamente;
ignoramos el elemento de índice 0). En estos casos, la solución del problema resultará
más fácil si utilizamos esa coincidencia.
Por ejemplo, vamos a realizar un programa que cuente el número de veces que
aparece cada una de las letras de un texto introducido por el teclado y a continuación
imprima el resultado. Para hacer el ejemplo sencillo, vamos a suponer que el texto solo
contiene letras minúsculas del alfabeto inglés (no hay ni letras acentuadas, ni la ll, ni la
ñ). La solución podría ser de la forma siguiente:
Introducir un texto.
Para finalizar pulsar [Ctrl][z]
a b c d e f g h i j k l m n o p q r s t u v w x y z
9 1 1 3 5 0 0 0 9 0 0 6 4 6 3 0 0 1 11 2 2 0 0 0 1 1
c[car] = 10;
c[car]++;
Pero ¿qué pasa con los elementos c[0] a c[96]? Según hemos planteado el problema
inicial quedarían sin utilizar (el enunciado decía: con qué frecuencia aparecen los
caracteres de la ‘a’ a la ‘z’). Esto, aunque no presenta ningún problema, se puede
evitar así:
c[car – ‘a’]++;
Para car igual a ‘a’ se trataría del elemento c[0] y para car igual a ‘z’ se trataría del
elemento c[25]. De esta forma podemos definir una matriz de enteros justamente
con un número de elementos igual al número de caracteres de la ‘a’ a la ‘z’ (26
caracteres según la tabla ASCII). El primer elemento será el contador de ‘a’, el
segundo el de ‘b’, y así sucesivamente. Un contador es una variable que inicialmente
vale cero (suponiendo que la cuenta empieza desde uno) y que después se incrementa
en una unidad cada vez que ocurre el suceso que se desea contar.
import java.io.*;
try
{
// Leer el siguiente carácter del texto y contabilizarlo
while ((car = (char) System.in.read() != eof)
{
// Si el carácter leído está entre la ‘a’ y la ‘z’
// incrementar el contador correspondiente
if (car >= ‘a’ && car <= ‘z’)
c[car – ‘a’]++;
}
}
catch (IOException ignorada) {}
2.4. Map
La biblioteca Java proporciona una interfaz Map definida en el paquete java.util en la
que se basa la implementación de la clase HashMap que facilita la creación y
manipulación de matrices asociativas. Para definir una de estas matrices, la sintaxis a
utilizar es la siguiente:
Según lo expuesto, la matriz c del ejemplo anterior podría ser sustituida por:
En este caso, la matriz asociativa c está inicialmente vacía. Para añadir un elemento
(un par de valores) basta con hacer referencia al mismo y será añadido siempre y
cuando no exista. Por ejemplo:
Vemos que cuando se introduce un elemento cuya clave existe, el valor actual es
reemplazado por el valor nuevo; esto es, las claves no se duplican. Si lo que necesita es
un mapa ordenado por la clave, utilice la clase TreeMap. La funcionalidad que presenta
es análoga a la de HashMap.
y size con el mismo significado que en la clase ArrayList, el método remove para
eliminar el elemento que tiene la clave especificada, el método entrySet que
devuelve un conjunto Set (una colección) de objetos de la clase Map.Entry<K, V>
(pares clave-valor), etc. A continuación vemos un ejemplo que crea una agenda con
pares de elementos nombre-telefono:
3. CADENAS DE CARACTERES
Las cadenas de caracteres en Java son objetos de la clase String. Por ejemplo, la línea
de código siguiente visualiza el literal “Fin del proceso.”, para lo cual, Java
previamente lo convierte en un objeto String:
System.out.println(“Fin del proceso.”);
Igual que sucedía con las matrices numéricas, una matriz unidimensional de caracteres
puede ser iniciada en el momento de su definición. Por ejemplo:
Este ejemplo define cadena como una matriz de caracteres con cuatro elementos
(cadena[0] a cadena[3]) y asigna al primer elemento el carácter ‘a’, al segundo el
carácter ‘b’, al tercero el carácter ‘c’ y al cuarto el carácter ‘d’. Puesto que cada
carácter es un entero, el ejemplo anterior podría escribirse también así:
Hemos visto que una forma de leer un carácter del flujo in era utilizando el método
read de la clase System. Entonces, leer una cadena de caracteres supondrá ejecutar
repetidas veces la ejecución de read y almacenar cada carácter leído en la siguiente
posición libre de una matriz de caracteres.
Otra forma de leer una cadena de caracteres consiste en leer una línea de texto de un
flujo de la clase BufferedReader, conectado al flujo in, utilizando el método
readLine, y almacenarla en un objeto String. El método readLine lee hasta
encontrar el carácter ‘\r’, ‘\n’ o los caracteres ‘\r\n’ introducidos al pulsar la tecla
Entrar; estos caracteres son leídos pero no almacenados, simplemente son
interpretados como delimitadores. Por ejemplo:
Una matriz de caracteres también puede ser convertida en un objeto String, según se
muestra a continuación. Por ejemplo:
System.out.println(“Ayer”.concat(" llovió"));
System.out.println("Ayer”.concat(" llovió”.concat(“ mucho”));
En otras palabras, el método compareTo permite saber si una cadena está en orden
alfabético antes (es menor) o después (es mayor) que otra y el proceso que sigue es el
mismo que nosotros ejercitamos cuando lo hacemos mentalmente, comparar las
cadenas carácter a carácter. La comparación se realiza sobre los valores Unicode de
cada carácter. El siguiente ejemplo compara dos cadenas y escribe “abcde” porque
esta cadena está antes por orden alfabético.
El siguiente ejemplo, elimina los espacios en blanco que haya al principio y al final de
str1, verifica si str1 finaliza con “gh” y en caso afirmativo obtiene de str1 una
subcadena str2 igual a str1 menos el sufijo “gh”.
Del estudio de la clase String sabemos que un objeto de esta clase no es modificable,
se puede observar y comprobar que los métodos que actúan sobre un objeto String
con la intención de modificarlo, no lo modifican, sino que devuelven un objeto nuevo
con las modificaciones solicitadas. En cambio, un objeto StringBuffer es un objeto
modificable tanto en contenido como en tamaño.
El siguiente ejemplo crea un objeto StringBuffer con la cadena "Mes de del año",
a continuación inserta la cadena "Abril ” a partir de la posición 7, y finalmente añade
al final, la cadena representativa del entero 2012. El resultado será "Mes de Abril
del año 2012".
StringTokenizer cadena;
cadena = new StringTokenizer(“uno, dos, tres y cuatro”);
Para obtener los elementos de la cadena separados por los delimitadores, en este caso
predeterminados, utilizaremos los métodos hasMoreTokens para saber si hay más
elementos en la cadena, y nextToken para obtener el siguiente elemento. Por
ejemplo:
while (cadena.hasMoreTokens())
System.out.println(cadena.nextToken());
También se pueden especificar los delimitadores en el instante de construir el objeto
StringTokenizer. Por ejemplo, la siguiente línea de código especifica como
delimitadores la coma y el espacio en blanco:
La diferencia con respecto a la versión anterior es que ahora no aparece la coma como
parte integrante de los elementos, ya que se ha especificado como delimitador y los
delimitadores no aparecen. Si queremos que los delimitadores aparezcan como un
elemento más, basta especificar true como tercer argumento:
Según lo estudiado a lo largo de este tema podemos decir que cada elemento de una
matriz unidimensional es de un tipo primitivo, o bien una referencia a un objeto.
Entonces, ¿cómo procederíamos si necesitáramos almacenar las temperaturas medias
de cada día durante los 12 meses de un año?, o bien ¿cómo procederiamos si
necesitáramos almacenar la lista de nombres de los alumnos de una determinada
clase? Razonando un poco, llegaremos a la conclusión de que utilizar matrices
unidimensionales para resolver los problemas planteados supondrá posteriormente un
difícil acceso a los datos almacenados; esto es, responder a las preguntas: ¿cuál es la
temperatura media del 10 de mayo?, o bien ¿cuál es el nombre del alumno número 25
de la lista? será mucho más sencillo si los datos los almacenamos en forma de tabla.
Por lo tanto, una solución fácil para los problemas planteados exige el uso de matrices
de dos dimensiones.
Una matriz multidimensional, como su nombre indica, es una matriz de dos o más
dimensiones. Java no soporta matrices multidimensionales, pero se puede lograr la
misma funcionalidad declarando matrices de matrices; las cuales, a su vez, pueden
también contener matrices, y así sucesivamente, hasta llegar a obtener el número de
dimensiones deseadas.
donde tipo es un tipo primitivo entero o real. El número de elementos de una matriz
multidimensional es el producto de las dimensiones indicadas por expr1, expr2. Por
ejemplo, la línea de código siguiente crea una matriz de dos dimensiones con 2×3 = 6
elementos de tipo int:
A partir de la línea de código anterior, Java crea una matriz unidimensional m con 2
elementos m[0] y m[1] que son referencias a otras dos matrices unidimensionales de 3
elementos. Gráficamente podemos imaginario así:
Evidentemente, el tipo de los elementos de la matriz referenciada por m es int[] y el
tipo de los elementos de las matrices referenciadas por m[0] y m[1] es int. Además,
puede comprobar la existencia y la longitud de las matrices unidimensionales
referenciadas por m, m[0] y m[1] utilizando el código siguiente:
Desde nuestro punto de vista, cuando se trate de matrices de dos dimensiones, es más
fácil pensar en ellas como si de una tabla de f filas por c columnas se tratara. Por
ejemplo:
Para acceder a los elementos de la matriz m, puesto que se trata de una matriz de dos
dimensiones, utilizaremos dos subíndices, el primero indicará la fila y el segundo la
columna donde se localiza el elemento, según se puede observar en la figura anterior.
Por ejemplo, la primera sentencia del ejemplo siguiente asigna el valor x al elemento
que está en la fila 1, columna 2; y la segunda, asigna el valor de este elemento al
elemento m[0][1].
m[1][2] = x;
m[0][1] = m[l][2];
do
{
System.out.print(“Numero de Columnas de la matriz: ”');
ncols = (int) System.in.read();
}
while (ncols < 1); // no permitir un valor negativo
Por ejemplo, la línea de código siguiente crea una matriz de cadenas de caracteres de
nFilas filas por nCarsPorFila caracteres máximo por cada fila.
Para acceder a los elementos de la matriz m, puesto que se trata de una matriz de
cadenas de caracteres, utilizaremos sólo el primer subíndice, el que indica la fila. Sólo
utilizaremos dos subíndices cuando sea necesario acceder a un carácter individual. Por
ejemplo, la primera sentencia asigna una cadena de caracteres a m[0] desde el
teclado; la cadena tendrá nCarsPorFila caracteres como máximo y será almacenada
a partir de la posición 0 de m[0]. Y la segunda, reemplaza el último carácter leído en
m[0] por ‘\0’, puesto que read devuelve el número de caracteres leídos.
Es importante que asimilemos que m[0], m[1], etc. son cadenas de caracteres y que,
por ejemplo, m[1][3] es un carácter; el que está en la fila 1, columna 3. No obstante,
el trabajo con cadenas de caracteres resulta mucho más sencillo si se utilizan las clases
String o StringBuffer de la biblioteca de Java para almacenar las cadenas de
caracteres. Desde este punto de vista, una matriz de cadenas de caracteres es una
matriz unidimensional de objetos String o StringBuffer, o bien una colección de
esos objetos.
En el tema 3 expusimos cómo definir un método en una clase y explicamos cómo pasar
argumentos a un método. Recordemos que los objetos pasados a los parámetros de un
método son siempre referencias a dichos objetos, lo cual significa que cualquier
modificación que se haga a esos objetos dentro del método afecta al objeto original, y
las matrices son objetos. En cambio, las variables de un tipo primitivo se pasan por
valor, lo cual significa que se pasa una copia, por lo que cualquier modificación que se
haga a esas variables dentro del método no afecta a la variable original.
En más de una ocasión, trabajando con cadenas de caracteres hemos pasado como
argumento una matriz. Algunos de los métodos de la biblioteca Java también tienen
parámetros que son matrices de caracteres, por ejemplo read. Pero no hemos
estudiado nada análogo en el caso de matrices numéricas, lo cual es lógico porque
mientras una cadena de caracteres, por ejemplo “nombre”, es un objeto que
manejamos habitualmente como tal, no sucede lo mismo con una matriz numérica,
donde cualquier operación pasa por el acceso individual a sus elementos.
¿Cuál es el resultado? Que cuando el método main visualice los elementos de la matriz
m, éstos aparecerán con los cambios introducidos por el método
MultiplicarPorDosMatriz2D. Esto es, ambos métodos trabajan sobre la misma
matriz. En el caso de un objeto ArrayList la explicación seria análoga.
7. LA CLASE Arrays
La clase Arrays del paquete java.util contiene varios métodos static para
manipular matrices. Esta clase se deriva directamente de Object.
El método binarySearch permite buscar un valor en una matriz que esté ordenada
ascendentemente utilizando el algoritmo de búsqueda binaria. Se trata de un
algoritmo muy eficiente en cuanto a que el tiempo requerido para realizar una
búsqueda es muy pequeño. La sintaxis expresada de forma genérica para utilizar este
método es la siguiente:
donde m representa la matriz, clave es el valor que se desea buscar del mismo tipo
que los elementos de la matriz, y tipo es cualquier tipo de datos de los siguientes:
byte, char, short, int, long, float, double y Object.
El valor devuelto es un entero correspondiente al índice del elemento que coincide con
el valor buscado. Si el valor buscado no se encuentra, entonces el valor devuelto es:
- (punto de inserción) – 1. El valor de punto de inserción es el índice del
elemento de la matriz donde debería encontrarse el valor buscado. La expresión
“- (punto de inserción) – 1” garantiza que el índice devuelto será mayor o igual
que cero solo si el valor buscado es encontrado. Como ejemplo, analicemos el
siguiente código:
double[] a = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
int i;
i = Arrays.binarySearch(a, 25); // i = 3
i = Arrays.binarySearch(a, 27); // i = -5
i = Arrrays.binarySearch(a, 5); // i = -1
i = Arrays.binarySearch(a, 60); // i = -11
El método equals permite verificar si dos matrices son iguales. Dos matrices se
consideran iguales cuando ambas tienen el mismo número de elementos y en el
mismo orden. Asimismo, dos matrices también son consideradas iguales si sus
referencias valen null. La sintaxis para utilizar este método, expresada de forma
genérica, es la siguiente:
El valor devuelto será true si ambas matrices son iguales y false en caso contrario.
Como ejemplo, podemos probar los resultados que produce el siguiente código:
double[] a = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
double[] b = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
if(Arrays.equals(a, b))
System.out.println("Son iguales");
else
System.out.println("No son iguales" ):
El método fill permite asignar un valor a todos los elementos de una matriz, o bien a
cada elemento de un rango especificado. La sintaxis expresada de forma genérica para
utilizar este método es la siguiente:
double[] a = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
Arrays.fill (a, 0); // poner los elementos de la matriz a cero
El método sort permite ordenar los elementos de una matriz en orden ascendente
utilizando el algoritmo quicksort. Se trata de un algoritmo muy eficiente en cuanto a
que el tiempo requerido para realizar la ordenación es mínimo. La sintaxis expresada
de forma genérica para utilizar este método es la siguiente:
void sort(tipo[] m)
void sort(tipo[] m, int desdelnd, int hastaInd)
donde m es la matriz a ordenar. Cuando solo queramos ordenar un rango de
elementos, utilizaremos el segundo formato de sort donde desdelnd y hastalnd
definen los límites de ese rango. tipo es cualquier tipo de datos de los siguientes:
boolean, byte, char, short, int, long, float, double y Object. Como ejemplo,
podemos probar los resultados que produce el siguiente código:
double[] a ={55, 50, 45, 40, 35, 30, 25, 20, 15, 10};
Arrays.sort(a);
8. COLECCIONES
Las colecciones ArrayList y HasMap realizan más o menos la misma función que las
colecciones Vector y HashTable de versiones anteriores, pero presentan una
diferencia fundamental: no están sincronizadas (nos referimos a la seguridad en el
trabajo con hilos). Esta estrategia supone una ruptura con el pasado, ya que las
colecciones son frecuentemente utilizadas en ámbitos donde la sincronización no es
necesaria, e imponerla, como sucede cuando utilizamos colecciones sincronizadas
como Vector o HashTable, no beneficia, sino que penaliza a una aplicación durante su
ejecución. Más aun, una sincronización innecesaria puede terminar en un “abrazo
mortal” bajo ciertas circunstancias.
Ninguna de las colecciones está sincronizada. Cuando estas nuevas colecciones
requieran de sincronización podemos proporcionársela vía
Collections.synchronizedList o Collections.synchronizedMap, por poner
algún ejemplo:
List<Tipo> vista =
Collections.synchronizedList (new ArrayList<Tipo>()>;