Principio de implementación subyacente de HashSet de análisis de código fuente jdk7.0
1. Descripción general de HashSet
HashSet es una clase de implementación de la interfaz Set. Se utiliza para almacenar múltiples elementos. Las características de almacenamiento son desorden, sin subíndice y los elementos no se pueden repetir.
Al mismo tiempo, se permite null como elemento; y el hilo no es seguro porque los métodos internos son métodos asincrónicos.
En segundo lugar, la estructura de almacenamiento de HashSet
Para HashSet, la capa inferior se implementa en base a HashMap; el almacenamiento de datos de HashSet es usar HashMap para almacenar pares
Los datos correspondientes, las operaciones relacionadas también se implementan llamando al método HashMap, por lo que la implementación subyacente de HashSet es relativamente simple.
Tres, mecanismo de principio de implementación interno de HashSet (análisis de código fuente)
-
Los elementos básicos de HashSet
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; // 底层定义一个HashMap,用于存储HashSet的元素 private transient HashMap<E,Object> map; // 定义一个虚拟的Object对象,作为HashMap的value,被static final修饰 private static final Object PRESENT = new Object();
-
El método de construcción en HashSet:
/* 无参数的构造器,其实底层默认创建一个空的HashMap对象, 并使用默认的初始容量为16,加载因子为0.75 */ public HashSet() { map = new HashMap<>(); } /* 创建一个包含 Collection中的元素HashSet对象, 底层默认创建一个HashMap,容量为足以存储Collection中的元素, 同时采用默认的加载因子 0.75 */ public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /* 利用指定的容量和加载因子创建一个新的HashSet对象, 传递的参数作为HashMap的参数:代表容量和加载因子 */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } /* 利用指定的参数创建一个新的HashSet对象, 实际底层是以此参数创建一个HashMap对象,同时采用默认的 加载因子 */ public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); }
-
Implementación de métodos comunes en HashSet:
/* 往 HashSt中添加一个元素:添加成功-true;不成功-false 实际底层调用 HashMap中的put方法,将要添加的元素作为键, 用字段中定义的虚拟Object对象作为值,进行存储,当存储的 key在HashMap中已经存在(哈希值相同,同时调用equals方法 返回值为true)时,key不改变,但是会用新值覆盖旧值,但是 每一次添加时,value都是用的同一个虚拟 Object对象。 所以:向HashSet中添加元素时,如果此元素已经存在(判断 依据:哈希码值相同,同时equals方法的返回值为true),集合 中的元素不会发生改变,这也满足了Set的不重复性。 但是需要注意的就是,判别元素在HashSet中是否存在,在put 方法中调用了 hashCode方法 和 equals方法进行判断,所以 为了保证HashSet的元素不重复性,需要做到以下两点: (1) 覆盖 hashCode方法 原则:内容相同的对象返回相同的哈希码值,为了提高效率 内容不同的对象,尽可能的返回不同的哈希码值 (2) 覆盖equals方法 原则:内容相同的对象,结果返回true */ public boolean add(E e) { return map.put(e, PRESENT)==null; } /* 返回此Set中的元素个数: 实际底层调用了Map中的size方法返回Entry的数量,返回值 代表Set集合中元素的个数 */ public int size() { return map.size(); } /* 判断HashMap中是否为空: 实际底层调用HashMap的isEmpty方法判断是否为空 */ public boolean isEmpty() { return map.isEmpty(); } /* 判断HashSet中是否包含某一个元素: 底层实际调用HashMap的containsKey方法,判断 HashMap中是否包含此键 */ public boolean contains(Object o) { return map.containsKey(o); } /* 从HashSet中删除元素: 实际底层调用的是 HashMap的 remove方法 */ public boolean remove(Object o) { return map.remove(o)==PRESENT; } /* 将HashMap中的元素进行清除: 实际底层调用 Map中的remove方法 */ public void clear() { map.clear(); }
Análisis de la capa de aplicación: de acuerdo con el análisis del método put, al almacenar pares clave-valor en el HashMap, solo se considera la clave y el valor no se considera en absoluto. Solo calcula y determina la ubicación de almacenamiento de cada clave- par de valores (Entrada) basado en la clave. Para garantizar la no repetibilidad de las claves, debe hacer que las claves con el mismo contenido tengan la misma ubicación de almacenamiento, de modo que la condición if se establezca durante el bucle for (es necesario llamar al método equals en este proceso), para que las claves duplicadas correspondan a Valor, el nuevo valor reemplaza al antiguo; pero si se almacena cada par clave-valor, se obtiene el mismo subíndice de almacenamiento, por lo que la lista enlazada correspondiente al mismo subíndice de la matriz será muy larga , y se debe llamar al método igual para cada almacenamiento. Si el contenido de la clave de comparación específica es el mismo, se reducirá la eficiencia del almacenamiento. Por lo tanto, para mejorar la eficiencia, se asigna un subíndice de almacenamiento diferente a la clave con contenido diferente tanto como sea posible, para que los elementos del HashMap se puedan distribuir lo más uniformemente posible, es decir, cada ubicación Un elemento como sea posible.
Por lo tanto, si el elemento de un tipo personalizado se almacena en el HashSet (como la clave del HashMap), debe anular el método hashCode y el método equals:
(1) El principio de cubrir el método hashCode:
A. Debe asegurarse de que los elementos con el mismo contenido devuelvan el mismo valor de código hash
B. Para mejorar la eficiencia, en la medida de lo posible, devolver diferentes valores de código hash para elementos con diferentes contenidos.
(2) Método Equals: los objetos con el mismo contenido devuelven verdadero.
Cuatro, resumen relacionado
La naturaleza subyacente de HashSet es HashMap, así que primero comprenda el principio de implementación subyacente de HashMap y luego aprenda sobre HashSet
El principio de implementación subyacente.