Red de conocimiento informático - Conocimiento sistemático - Análisis en profundidad de la colección Set

Análisis en profundidad de la colección Set

Las principales características de las colecciones Set son: los elementos no se repiten y el almacenamiento es desordenado.

Abra la colección Set. Las principales clases de implementación incluyen HashSet, LinkedHashSet, TreeSet, EnumSet (RegularEnumSet, JumboEnumSet), etc. Para resumir las clases de implementación de la interfaz Set, la figura es la siguiente:

De la figura En cuanto a la relación de herencia, podemos saber que las principales clases de implementación de la interfaz Set incluyen AbstractSet, HashSet, LinkedHashSet, TreeSet y EnumSet (RegularEnumSet, JumboEnumSet y EnumSet son clases abstractas). agregado en jdk1.5 La diferencia es que los elementos de la colección EnumSet deben ser de tipo enumeración.

HashSet es un conjunto con entrada y salida desordenadas. Los elementos del conjunto se basan en la implementación clave de HashMap y los elementos no se pueden repetir.

LinkedHashSet es un conjunto con; entrada y salida ordenadas en el conjunto Los elementos se basan en la implementación clave de LinkedHashMap y los elementos no son repetibles

TreeSet es una colección ordenada y los elementos de la colección se basan en la clave; implementación de TreeMap, y los mismos elementos no son repetibles;

EnumSet Es una colección de conjuntos especial que se usa junto con los tipos de enumeración RegularEnumSet y JumboEnumSet no se pueden crear instancias por separado y solo pueden generarse mediante EnumSet. no se puede repetir;

¡Analicemos las principales clases de implementación a continuación!

HashSet es un conjunto desordenado de entrada y salida. La capa inferior se implementa en base a HashMap. Puede ver esto en el código fuente. de la siguiente manera:

p>

clase pública HashSetlt;Egt;

extiende AbstractSetlt;Egt;

implementa Setlt;Egt;, Cloneable, java. io.Serializable{

}

Abra el método add() de HashSet, el código fuente es el siguiente:

public boolean add(E e) {

//Agregar elementos a HashMap

return map.put(e, PRESENT)==null

}

La variable; PRESENT es un objeto no vacío. El código fuente es el siguiente:

p>

objeto final estático privado PRESENT = new Object()

Se puede analizar que. cuando se ejecuta add(), es equivalente a

HashMap map = new HashMaplt ;gt;();

map.put(e, new Object()); e representa el elemento que se agregará

En el artículo de la colección anterior, aprendimos que cuando HashMap agrega elementos, utiliza los métodos equals() y hashCode() para determinar si las claves entrantes son las mismas. son iguales, entonces HashMap piensa que los elementos agregados son el mismo elemento; de lo contrario, no lo es.

Se puede ver en el análisis del código fuente que HashSet utiliza esta característica de HashMap para lograr las características de almacenar subíndices de elementos desordenados y elementos que no se repiten.

Lo mismo ocurre con el método de eliminación de HashSet, que también se basa en la implementación subyacente de HashMap. El código fuente es el siguiente:

public boolean remove(Object o). {

//Llamar al método remove de HashMap elimina elementos

return map.remove(o)==PRESENT;

}

HashSet no proporciona el método get como List y Map, pero usa un iterador o un bucle for para recorrer los elementos de la siguiente manera:

public static void main(String[] args) {

Setlt; Stringgt; hashSet = new HashSetlt ();

System.out.println("Tamaño de capacidad inicial de HashSet: " hashSet.size()); .add("1");

hashSet.add("2");

hashSet.add("3"); ("3");

hashSet.add("2");

hashSet.add(nulo

}

Resultado de salida:

Capacidad inicial de HashSet: 0

Capacidad de HashSet: 4

nulo, 1, 2, 3,

= ======= ===

null, 1, 2, 3,

Cabe señalar que HashSet permite agregar elementos nulos.

LinkedHashSet es un conjunto ordenado de entradas y salidas, heredado de HashSet, pero la implementación subyacente se basa en LinkedHashMap.

Si ha aprendido sobre LinkedHashMap antes, debe saber que también hereda de HashMap. La única diferencia es que la estructura de datos subyacente de LinkedHashMap se basa en una lista enlazada circular y la matriz especifica el. cabeza y cola Aunque los subíndices de la matriz se almacenan de manera desordenada, el proceso de almacenamiento de elementos se puede consultar a través del principio y la cola de la matriz, además de una lista enlazada circular, logrando así una entrada y salida ordenadas.

Si aún no comprende el proceso de implementación de LinkedHashMap, puede consultar los artículos sobre el proceso de implementación de LinkedHashMap en la serie de colecciones.

Lea el código fuente de LinkedHashSet, la definición de clase es la siguiente:

clase pública LinkedHashSetlt;

extiende Egt;

implementa Setlt ;Egt;, Cloneable, java.io.Serializable {

}

Consulta el código fuente, el método llamado por super, el código fuente es el siguiente:

HashSet(int inicialCapacity, float loadFactor, boolean dummy) {

//Inicializar un LinkedHashMap

map = new LinkedHashMaplt gt;

}

LinkedHshSet no anula el método add, sino que llama directamente al método add() de HashSet. Debido a que la clase de implementación del mapa es LinkedHashMap, aquí se agregan elementos a LinkedHashMap. Cuando se realiza add(), es equivalente en

HashMap map = new LinkedHashMaplt (); /e representa el elemento que se agregará

LinkedHashSet no anula el método de eliminación, sino que llama directamente al método de eliminación de HashSet. Debido a que LinkedHashMap no anula el método de eliminación, también llama al método de eliminación de HashMap. El código fuente es el siguiente:

public boolean remove (Object o) {

//Llame al método remove de HashMap para eliminar el elemento

return map .remove(o)==PRESENT;

}

De manera similar, LinkedHashSet no proporciona un método get. Utilice un iterador o un bucle for para recorrer los elementos. :

public static void main(String[] args) {

Setlt; linkedHashSet = new LinkedHashSet (); .println("tamaño de capacidad inicial de linkedHashSet: " linkedHashSet.size()

linkedHashSet.add("1");

linkedHashSet.add("2");

linkedHashSet.add("3");

linkedHashSet .add("3");

linkedHashSet.add("2");

linkedHashSet.add(null);

linkedHashSet.add(null

}

Resultado de salida:

Capacidad inicial de linkedHashSet: 0

Tamaño de capacidad de linkedHashSet: 4

1, 2, 3, nulo,

===========

1, 2, 3 , null,

Se puede ver que, en comparación con HashSet, la entrada y salida de LinkedHashSet están en orden.

TreeSet es un conjunto ordenado que implementa las interfaces NavigableSet, SortedSet y Set, y se implementa en base a TreeMap. TreeSet usa el elemento clave en TreeMap para almacenar elementos. También podemos ver esto en el código fuente, la definición de clase es la siguiente:

public class Egt extiende AbstractSetlt; ;

implementa NavigableSetlt; Egt;, Cloneable, java.io.Serializable {

}

new TreeSetlt;gt () significa cuando se crea una instancia del objeto. , Se puede simplificar de la siguiente manera:

NavigableMaplt; E, Objectgt; m = new TreeMaplt; E, Objectgt (); problema.

clase pública TreeMaplt; K, Vgt;

extiende AbstractMaplt; K, Vgt;

implementa NavigableMaplt;, Cloneable, java.io. Serializable{

...

}

Abra el método add() de TreeSet, el código fuente es el siguiente:

public boolean add(E e) {

//Agregar elementos a TreeMap

return m.put(e, PRESENT)==null;

}

La variable PRESENT también es un objeto no vacío. La parte del código fuente es la siguiente:

Private static final Object PRESENT = new Object();

Se puede analizar que al realizar add(), es equivalente a

TreeMap map = new TreeMaplt (); new Object()); //e Indica los elementos que se agregarán

La función principal de la clase TreeMap es ordenar los elementos de la colección agregados de acuerdo con una regla. Por supuesto, la clasificación también se puede personalizar. Por ejemplo, el método de prueba es el siguiente:

public static void main(String[] args) {

Map initMap = new TreeMap. ();

initMap.put("4", " d");

initMap.put("3", "c");

initMap.put("1", "a");

initMap.put("2", "b");

//Clasificación natural predeterminada, la clave es orden ascendente

System.out.println("Resultado de clasificación predeterminado:" initMap .toString());

//Clasificación personalizada, pase el objeto interno Comparator durante la fase de inicialización de TreeMap

Mapa comparadorMap = new TreeMaplt; String, Stringgt (new Comparatorlt; Stringgt ;() {

@Override

public int compare(String o1, String o2 ){

//Compare el tamaño según la clave, use flashback y use la clasificación de grande a pequeño

return o2.compareTo(o1);

}

});

comparatorMap.put("4", "d");

comparatorMap.put("3", "c" );

comparatorMap.put("1", "a");

comparatorMap.put("2", "b");

Sistema .out.println(

"Resultados de clasificación personalizados:" comparatorMap.toString());

}

Resultados de salida:

Resultados de clasificación predeterminados: {1=a, 2= b , 3=c, 4=d}

Resultados de clasificación personalizados: {4=d, 3=c, 2=b, 1=a}

Creo que he usado TreeMap Amigos, deben saber que TreeMap ordenará automáticamente las claves de acuerdo con ciertas reglas. TreeSet utiliza las características de TreeMap para realizar la colección de elementos agregados. Al generar, los resultados ya están ordenados.

Si no ha visto el proceso de implementación de TreeMap en el código fuente, puede consultar la introducción al proceso de implementación de TreeMap en la serie de artículos de la colección o leer el código fuente de jdk.

Lo mismo ocurre con el método de eliminación de TreeSet, que también se basa en la implementación subyacente de TreeMap. El código fuente es el siguiente:

public boolean remove(Object o). {

//Llame al método remove de TreeMap, elimina elementos

return m.remove(o)==PRESENT;

}

TreeSet no anula el método get, pero utiliza un iterador o un bucle for para recorrer elementos de la siguiente manera:

public static void main(String[] args) {

Setlt; treeSet = new TreeSetlt; gt;

System.out.println("tamaño de capacidad inicial de treeSet: " treeSet.size()); ");

treeSet.add("4");

treeSet.add("3");

treeSet.add("8") ;

treeSet.add("5");

}

Resultado de salida:

capacidad inicial de treeSet: 0

capacidad del árbol: 5

1, 3, 4, 5, 8,

===========

1, 3, 4, 5, 8,

Hay dos formas de utilizar la clasificación personalizada. La primera es implementar la interfaz Comparable en la clase de elemento que debe agregarse y anular el método compareTo. comparar elementos e implementar una clasificación personalizada.

/**

Cree una clase de entidad Persona, implemente la interfaz Comparable, anule el método compareTo e implemente un método de prueba de clasificación personalizado a través de la variable edad de la siguiente manera:

public static void main(String[] args) {

Setlt; treeSet = new TreeSetlt (); Tamaño de capacidad inicial de treeSet:" treeSet.size());

treeSet.add(new Person("李一", 18));

treeSet.add(new Person( "李二" , 17));

treeSet.add(nueva Persona("李三", 19));

treeSet.add(nueva Persona("李思" , 21)) ;

treeSet.add(new Person("李五", 20));

}

Resultado de salida:

Capacidad inicial de treeSet: 0

Capacidad de treeSet: 5

Resultados de clasificación personalizados según edad:

Li 2: 17, Li 1: 18, Li San: 19, Li Wu: 20, Li Si: 21,

El segundo método es que durante la fase de inicialización de TreeSet, Person no necesita implementar la interfaz Comparable y usa la interfaz Comparator como interfaz interna. class como parámetro. , inicialícelo de la siguiente manera:

public static void main(String[] args) {

//Clasificación personalizada

Setlt; ; treeSet = new TreeSetlt;gt;(new Comparatorlt;Persongt;(){

@Override

public int compare(Persona o1, Persona o2) {

if( o1 == null || o2 == null){

//No es necesario comparar

return 0;

}

// Ordenar de pequeño a grande

return o1.getAge() - o2.getAge();

}

});

System.out.println("capacidad inicial de treeSet:" treeSet.size());

treeSet.add(new Person("李一", 18));

treeSet.add(nueva Persona("李二", 17));

treeSet.add(nueva Persona("李三", 19)); treeSet.add( nueva Persona("李四", 21));

treeSet.add(nueva Persona("李五", 20)); >

Resultado de salida:

treeSet capacidad inicial

Tamaño: 0

Tamaño de capacidad del árbol: 5

Resultados de clasificación personalizados según la edad:

Li 2: 17, Li 1: 18, Li San: 19 , Li Wu: 20, Li Si: 21,

Cabe señalar que TreeSet no puede agregar elementos vacíos; de lo contrario, se informará un error de puntero nulo.

EnumSet es una colección de conjuntos especializada que se utiliza con tipos de enumeración, heredada de la clase abstracta AbstractSet. A diferencia de HashSet, LinkedHashSet y TreeSet, los elementos EnumSet deben ser del tipo Enum y todos los elementos deben ser del mismo tipo de enumeración. El código fuente de la definición de EnumSet es el siguiente:

clase abstracta pública EnumSetlt; E extiende Enumlt ;Egt;gt; extiende AbstractSetlt;Egt;

implementa Cloneable, java.io.Serializable {

......

}

EnumSet es una clase virtual y no puede obtener objetos directamente mediante la creación de instancias. Solo puede devolver instancias de la clase de implementación EnumSet a través de los métodos estáticos que proporciona.

Hay dos clases de implementación de EnumSet, a saber, RegularEnumSet y JumboEnumSet. Ambas clases de implementación heredan de EnumSet.

EnumSet decidirá qué clase de implementación devolver en función de la cantidad de elementos en el tipo de enumeración. Cuando la cantidad de elementos en el elemento EnumSet es menor o igual a 64, devolverá una instancia de RegularEnumSet; cuando el elemento EnumSet Si el número es mayor que 64, se devolverá una instancia de JumboEnumSet.

Podemos ver esto en el código fuente, que es el siguiente:

public static lt; E extends Egt; EnumSetlt; elementType) {

Enumlt;?gt; universo = getUniverse(elementType);

if (universe == null)

throw new ClassCastException(elementType) " not an enum");

//Cuando el número de elementos es menor o igual a 64, devuelve RegularEnumSet

if (universe.length lt; = 64)

devuelve nuevo RegularEnumSetlt; gt; (elementType, universo

else

// Mayor que 64, devuelve JumboEnumSet

devuelve nuevo JumboEnumSetlt; ; gt; (elementType, universo);

}

noneOf es un método estático en EnumSet, utilizado para determinar qué clase de implementación se devuelve.

Echemos un vistazo a la clase que usa RegularEnumSet cuando el número de elementos es menor o igual a 64. El código fuente es el siguiente:

class RegularEnumSetlt E extends Enumlt; ; Egt; gt; extiende EnumSetlt; {

}

RegularEnumSet obtiene el resultado a través de operaciones binarias y usa long directamente para almacenar elementos.

Echemos un vistazo a la clase que usa JumboEnumSet cuando el número de elementos es mayor que 64. El código fuente es el siguiente:

class JumboEnumSetlt E extends Enumlt; ; extiende EnumSetlt; Egt ; {

}

JumboEnumSet también obtiene resultados a través de operaciones binarias y usa long para almacenar elementos, pero usa matrices para almacenar elementos.

En comparación con los dos, RegularEnumSet es más eficiente que JumboEnumSet porque hay menos pasos de operación. En la mayoría de los casos, se devuelve RegularEnumSet. Solo cuando el número de elementos enumerados exceda 64, se utilizará JumboEnumSet.

Agregar elemento:

//Crea un nuevo tipo de enumeración de EnumEntity y define 2 parámetros

public enum EnumEntity {

MUJER, MAN;

}

Crear un EnumSet vacío:

//Crear un EnumSet con contenido vacío

EnumEntitygt; EnumSet.noneOf(EnumEntity.class);

System.out.println(noneSet

Resultado de salida:

[]

Cree un EnumSet y agregue todos los elementos del tipo de enumeración:

//Cree un EnumSet y agregue el contenido del elemento EnumEntity al EnumSet

EnumSetlt; = EnumSet.allOf(EnumEntity.class);

System.out.println(allSet);

Resultado de salida:

[MUJER, HOMBRE]

p>

Cree un EnumSet y agregue el elemento de enumeración especificado:

//Cree un EnumSet y agregue MUJER al EnumSet

EnumSetlt; customSet = EnumSet .of( EnumEntity.WOMAN);

System.out.println(customSet);

Elementos de consulta

EnumSet es lo mismo que HashSet, LinkedHashSet y TreeSet. o bucle for para recorrer los elementos, el método es el siguiente:

EnumSetlt; EnumEntitygt; allSet = EnumSet.allOf(EnumEntity.class);

for (EnumEntity enumEntity: allSet) {

System.out.print(enumEntity ",");

}

Resultado de salida:

MUJER, HOMBRE,

HashSet es una colección de conjuntos con entrada y salida desordenada. Los elementos no se repiten. La capa inferior se implementa en función de la clave de HashMap. Si el elemento agregado es un objeto. El objeto necesita anular el método equals() y hashCode() para restringir si son los mismos elementos.

LinkedHashSet es una colección de conjuntos con entrada y salida ordenadas. Hereda de HashSet. Los elementos no se repiten. La capa subyacente se implementa en función de la clave de LinkedHashMap. una estructura de lista circular enlazada para garantizar que la secuencia de entrada y salida sea coherente.

TreeSet es una colección de conjuntos ordenada. Los elementos no se pueden repetir. La capa inferior se implementa según la clave de TreeMap. Los elementos no se pueden almacenar en orden natural. También utilice las interfaces Comparable y Comparator para comparar tamaños y así implementar una clasificación personalizada.

EnumSet es una colección de conjuntos especial utilizada con tipos de enumeración, agregada en jdk1.5. EnumSet es una clase virtual con dos clases de implementación, RegularEnumSet y JumboEnumSet. No se puede crear una instancia ni cambiar explícitamente. EnumSet decidirá dinámicamente qué clase de implementación usar. Cuando el número de elementos es menor o igual a 64, se usa RegularEnumSet; es mayor que 64 Cuando se usa, use la clase JumboEnumSet. EnumSet se implementa internamente usando vectores de bits y tiene un rendimiento de tiempo y espacio extremadamente alto. Si los elementos son tipos enumerados, se recomienda usar EnumSet.

1. Código fuente JDK1.7amp; JDK1.8

2. Programa Park-java collection-EnumMap y EnumSet

3. Tecnología geek de Java-/ javageektech/article/details/103077788