Recientemente, vi una muy buena descripción del marco de colección en un libro sobre J2EE. La filtré y la publiqué para compartirla con todos. El marco de colección proporciona interfaces y clases para administrar colecciones de objetos. A continuación se muestra la descripción de cada componente.
Interfaz de colección
Colección es la interfaz de colección más básica. Una Colección representa un conjunto de Objetos, es decir, los elementos de la Colección. Algunas Colecciones permiten elementos idénticos y otras no. Algunos tipos y otros no. El SDK de Java no proporciona clases que heredan directamente de la Colección. Las clases proporcionadas por el SDK de Java son todas "subinterfaces" que heredan de la Colección, como List y Set.
Todas las clases que implementan la interfaz Colección deben proporcionar dos constructores estándar: un constructor sin parámetros para crear una Colección vacía y un constructor con parámetros de Colección para crear una nueva Colección. La Colección de entrada tiene los mismos elementos. El último constructor permite al usuario copiar una Colección.
¿Cómo iterar a través de cada elemento de la Colección? Independientemente del tipo real de Colección, admite un método iterador (), que devuelve un iterador que se puede utilizar para acceder a cada elemento de la Colección uno por uno. El uso típico es el siguiente:
Copie el código de código de la siguiente manera:
Iterador it = collection.iterator(); // Obtener un iterador
mientras(es.tieneNext()) {
Objeto obj = it.next(); // Obtener el siguiente elemento
}
Las dos interfaces derivadas de la interfaz Colección son Lista y Conjunto.
Interfaz de lista La lista es una colección ordenada. Con esta interfaz, puede controlar con precisión la posición de inserción de cada elemento. Los usuarios pueden acceder a los elementos de la Lista utilizando el índice (la posición del elemento en la Lista, similar a un subíndice de matriz), que es similar a una matriz de Java.
A diferencia del Conjunto que se menciona a continuación, List permite los mismos elementos.
Además del método iterator() necesario para la interfaz Collection, List también proporciona un método listIterator(), que devuelve una interfaz ListIterator. En comparación con la interfaz Iterator estándar, ListIterator tiene más métodos add() y otros, lo que permite adiciones. Eliminar, establecer elementos y recorrer hacia adelante o hacia atrás.
Las clases comunes que implementan la interfaz List son LinkedList, ArrayList, Vector y Stack.
Clase de lista enlazada LinkedList implementa la interfaz List y permite elementos nulos. Además, LinkedList proporciona métodos adicionales de obtención, eliminación e inserción al principio o al final de LinkedList. Estas operaciones permiten que LinkedList se utilice como pila, cola o deque.
Tenga en cuenta que LinkedList no tiene métodos sincronizados. Si varios subprocesos acceden a una Lista al mismo tiempo, deben implementar la sincronización de acceso ellos mismos. Una solución alternativa es construir una Lista sincronizada al crear la Lista:
Lista lista = Collections.synchronizedList(new LinkedList(...));
clase ArrayList ArrayList implementa matrices de tamaño variable. Permite todos los elementos, incluido nulo. ArrayList no está sincronizado.
El tiempo de ejecución de los métodos size, isEmpty, get y set es constante. Sin embargo, el costo del método de suma es una constante amortizada y agregar n elementos requiere O(n) tiempo. Otros métodos tienen un tiempo de ejecución lineal.
Cada instancia de ArrayList tiene una capacidad (Capacidad), que es el tamaño de la matriz utilizada para almacenar elementos. Esta capacidad aumenta automáticamente a medida que se agregan nuevos elementos, pero el algoritmo de crecimiento no está definido. Cuando es necesario insertar una gran cantidad de elementos, se puede llamar al método sureCapacity para aumentar la capacidad de ArrayList antes de insertarlo para mejorar la eficiencia de la inserción.
Al igual que LinkedList, ArrayList tampoco está sincronizado.
clase vectorial Vector es muy similar a ArrayList, pero Vector está sincronizado. Aunque el Iterador creado por Vector tiene la misma interfaz que el Iterador creado por ArrayList, debido a que Vector está sincronizado, cuando se crea y se utiliza un Iterador, otro hilo cambia el estado del Vector (por ejemplo, agregando o eliminando algún elemento) , Se generará ConcurrentModificationException al llamar al método Iterator, por lo que se debe detectar la excepción.
clase de pila Stack hereda de Vector e implementa una pila de último en entrar, primero en salir. Stack proporciona 5 métodos adicionales que permiten utilizar Vector como pila. Los métodos básicos push y pop, así como el método peek, colocan el elemento en la parte superior de la pila, el método vacío prueba si la pila está vacía y el método de búsqueda detecta la posición de un elemento en la pila. La pila es una pila vacía después de su creación.
Establecer interfaz Set es una Colección que no contiene elementos duplicados, es decir, dos elementos cualesquiera e1 y e2 tienen e1.equals(e2)=false, y Set tiene como máximo un elemento nulo.
Obviamente, el constructor Set tiene la restricción de que el parámetro Collection pasado no puede contener elementos duplicados.
Tenga en cuenta: los objetos mutables deben manipularse con cuidado. Si un elemento mutable en un Conjunto cambia su estado causando Object.equals(Object)=true, causará algunos problemas.
Interfaz de mapa <BR>Tenga en cuenta que Map no hereda la interfaz de Colección. Map proporciona una asignación de clave a valor. Un mapa no puede contener la misma clave y cada clave solo puede asignar un valor. La interfaz del Mapa proporciona tres tipos de vistas de conjuntos. El contenido del Mapa puede considerarse como un conjunto de conjuntos de claves, un conjunto de conjuntos de valores o un conjunto de asignaciones de valores clave.
clase de tabla hash Hashtable hereda la interfaz Map e implementa una tabla hash de mapeo clave-valor. Cualquier objeto no nulo se puede utilizar como clave o valor.
Para agregar datos, use put (clave, valor) y para eliminar datos, use get (clave). El costo de tiempo de estas dos operaciones básicas es constante.
Hashtable ajusta el rendimiento a través de dos parámetros: capacidad inicial y factor de carga. Normalmente, el factor de carga predeterminado de 0,75 logra un mejor equilibrio entre tiempo y espacio. Aumentar el factor de carga puede ahorrar espacio, pero el tiempo de búsqueda correspondiente aumentará, lo que afectará operaciones como get y put.
Un ejemplo simple del uso de Hashtable es el siguiente. Coloque 1, 2 y 3 en Hashtable, y sus claves son "uno", "dos" y "tres" respectivamente:
Copie el código de código de la siguiente manera:
Números de tabla hash = nueva tabla hash();
números.put(“uno”, nuevo entero(1));
números.put(“dos”, nuevo entero(2));
números.put(“tres”, nuevo entero(3));
Para recuperar un número, como 2, utilice la clave correspondiente:
Entero n = (Entero)números.get(“dos”);
System.out.println(“dos =” + n);
Dado que el objeto utilizado como clave determinará la posición del valor correspondiente calculando su función hash, cualquier objeto utilizado como clave debe implementar los métodos hashCode y equals. Los métodos hashCode y equals heredan de la clase raíz Object. Si usa una clase personalizada como clave, tenga mucho cuidado de acuerdo con la definición de la función hash, si los dos objetos son iguales, es decir, obj1.equals (. obj2)=true, entonces su código hash debe ser Lo mismo, pero si dos objetos son diferentes, su código hash no es necesariamente diferente. Si el código hash de dos objetos diferentes es el mismo, este fenómeno se llama conflicto. El conflicto aumentará el costo de tiempo de operación de la tabla hash. para definirlo bien. El método hashCode() puede acelerar las operaciones de la tabla hash.
Si el mismo objeto tiene un código hash diferente, la operación de la tabla hash tendrá resultados inesperados (el método get esperado devuelve nulo). Para evitar este problema, solo necesita recordar una cosa: anular el método igual y el método hashCode al mismo tiempo. tiempo. No escribas solo uno de ellos.
Hashtable es sincrónico.
Clase HashMap HashMap es similar a Hashtable, excepto que HashMap es asíncrono y permite valores nulos, es decir, valores nulos y claves nulas. , pero cuando se trata a HashMap como una Colección (el método de valores() puede devolver una Colección), el tiempo de sobrecarga de sus suboperaciones de iteración es proporcional a la capacidad de HashMap. Por lo tanto, si el rendimiento de las operaciones iterativas es muy importante, no establezca la capacidad inicial de HashMap demasiado alta ni el factor de carga demasiado bajo.
Clase WeakHashMap WeakHashMap es un HashMap mejorado que implementa "referencias débiles" a las claves. Si ya no se hace referencia a una clave externamente, GC puede reciclarla.
Resumen <BR>Si están involucradas operaciones como pilas y colas, debe considerar usar List. Si necesita insertar y eliminar elementos rápidamente, debe usar LinkedList. Si necesita un acceso aleatorio rápido a los elementos, debe usar ArrayList.
Si el programa está en un entorno de un solo subproceso, o el acceso se realiza solo en un subproceso, considere las clases asincrónicas, que son más eficientes. Si varios subprocesos pueden operar una clase al mismo tiempo, se deben usar clases sincronizadas.
Preste especial atención al funcionamiento de la tabla hash. El objeto utilizado como clave debe anular correctamente los métodos igual y hashCode.
Intente devolver la interfaz en lugar del tipo real, como devolver List en lugar de ArrayList, de modo que si necesita reemplazar ArrayList con LinkedList en el futuro, no sea necesario cambiar el código del cliente. Esto es programación para la abstracción.