Mi información de contacto
Correo[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Tabla de contenido
1: proceso de método de colocación
- public V put(K key, V value) {
- return putVal(hash(key), key, value, false, true);
- }
-
- final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
- boolean evict) {
- Node<K,V>[] tab; Node<K,V> p; int n, i;
- //判断数组是否未初始化
- if ((tab = table) == null || (n = tab.length) == 0)
- //如果未初始化,调用resize方法 进行初始化
- n = (tab = resize()).length;
- //通过 & 运算求出该数据(key)的数组下标并判断该下标位置是否有数据
- if ((p = tab[i = (n - 1) & hash]) == null)
- //如果没有,直接将数据放在该下标位置
- tab[i] = newNode(hash, key, value, null);
- //该数组下标有数据的情况
- else {
- Node<K,V> e; K k;
- //判断该位置数据的key和新来的数据是否一样
- if (p.hash == hash &&
- ((k = p.key) == key || (key != null && key.equals(k))))
- //如果一样,证明为修改操作,该节点的数据赋值给e,后边会用到
- e = p;
- //判断是不是红黑树
- else if (p instanceof TreeNode)
- //如果是红黑树的话,进行红黑树的操作
- e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
- //新数据和当前数组既不相同,也不是红黑树节点,证明是链表
- else {
- //遍历链表
- for (int binCount = 0; ; ++binCount) {
- //判断next节点,如果为空的话,证明遍历到链表尾部了
- if ((e = p.next) == null) {
- //把新值放入链表尾部
- p.next = newNode(hash, key, value, null);
- //因为新插入了一条数据,所以判断链表长度是不是大于等于8
- if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
- //如果是,进行转换红黑树操作
- treeifyBin(tab, hash);
- break;
- }
- //判断链表当中有数据相同的值,如果一样,证明为修改操作
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- break;
- //把下一个节点赋值为当前节点
- p = e;
- }
- }
- //判断e是否为空(e值为修改操作存放原数据的变量)
- if (e != null) { // existing mapping for key
- //不为空的话证明是修改操作,取出老值
- V oldValue = e.value;
- //一定会执行 onlyIfAbsent传进来的是false
- if (!onlyIfAbsent || oldValue == null)
- //将新值赋值当前节点
- e.value = value;
- afterNodeAccess(e);
- //返回老值
- return oldValue;
- }
- }
- //计数器,计算当前节点的修改次数
- ++modCount;
- //当前数组中的数据数量如果大于扩容阈值
- if (++size > threshold)
- //进行扩容操作
- resize();
- //空方法
- afterNodeInsertion(evict);
- //添加操作时 返回空值
- return null;
- }
- public V get(Object key) {
- Node<K,V> e;
- //hash(key),获取key的hash值
- //调用getNode方法,见下面方法
- return (e = getNode(hash(key), key)) == null ? null : e.value;
- }
-
-
- final Node<K,V> getNode(int hash, Object key) {
- Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
- //找到key对应的桶下标,赋值给first节点
- if ((tab = table) != null && (n = tab.length) > 0 &&
- (first = tab[(n - 1) & hash]) != null) {
- //判断hash值和key是否相等,如果是,则直接返回,桶中只有一个数据(大部分的情况)
- if (first.hash == hash && // always check first node
- ((k = first.key) == key || (key != null && key.equals(k))))
- return first;
-
- if ((e = first.next) != null) {
- //该节点是红黑树,则需要通过红黑树查找数据
- if (first instanceof TreeNode)
- return ((TreeNode<K,V>)first).getTreeNode(hash, key);
-
- //链表的情况,则需要遍历链表查找数据
- do {
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- } while ((e = e.next) != null);
- }
- }
- return null;
- }
- //扩容、初始化数组
- final Node<K,V>[] resize() {
- Node<K,V>[] oldTab = table;
- //如果当前数组为null的时候,把oldCap老数组容量设置为0
- int oldCap = (oldTab == null) ? 0 : oldTab.length;
- //老的扩容阈值
- int oldThr = threshold;
- int newCap, newThr = 0;
- //判断数组容量是否大于0,大于0说明数组已经初始化
- if (oldCap > 0) {
- //判断当前数组长度是否大于最大数组长度
- if (oldCap >= MAXIMUM_CAPACITY) {
- //如果是,将扩容阈值直接设置为int类型的最大数值并直接返回
- threshold = Integer.MAX_VALUE;
- return oldTab;
- }
- //如果在最大长度范围内,则需要扩容 OldCap << 1等价于oldCap*2
- //运算过后判断是不是最大值并且oldCap需要大于16
- else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
- oldCap >= DEFAULT_INITIAL_CAPACITY)
- newThr = oldThr << 1; // double threshold 等价于oldThr*2
- }
- //如果oldCap<0,但是已经初始化了,像把元素删除完之后的情况,那么它的临界值肯定还存在, 如果是首次初始化,它的临界值则为0
- else if (oldThr > 0) // initial capacity was placed in threshold
- newCap = oldThr;
- //数组未初始化的情况,将阈值和扩容因子都设置为默认值
- else { // zero initial threshold signifies using defaults
- newCap = DEFAULT_INITIAL_CAPACITY;
- newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
- }
- //初始化容量小于16的时候,扩容阈值是没有赋值的
- if (newThr == 0) {
- //创建阈值
- float ft = (float)newCap * loadFactor;
- //判断新容量和新阈值是否大于最大容量
- newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
- (int)ft : Integer.MAX_VALUE);
- }
- //计算出来的阈值赋值
- threshold = newThr;
- @SuppressWarnings({"rawtypes","unchecked"})
- //根据上边计算得出的容量 创建新的数组
- Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
- //赋值
- table = newTab;
- //扩容操作,判断不为空证明不是初始化数组
- if (oldTab != null) {
- //遍历数组
- for (int j = 0; j < oldCap; ++j) {
- Node<K,V> e;
- //判断当前下标为j的数组如果不为空的话赋值个e,进行下一步操作
- if ((e = oldTab[j]) != null) {
- //将数组位置置空
- oldTab[j] = null;
- //判断是否有下个节点
- if (e.next == null)
- //如果没有,就重新计算在新数组中的下标并放进去
- newTab[e.hash & (newCap - 1)] = e;
- //有下个节点的情况,并且判断是否已经树化
- else if (e instanceof TreeNode)
- //进行红黑树的操作
- ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
- //有下个节点的情况,并且没有树化(链表形式)
- else {
- //比如老数组容量是16,那下标就为0-15
- //扩容操作*2,容量就变为32,下标为0-31
- //低位:0-15,高位16-31
- //定义了四个变量
- // 低位头 低位尾
- Node<K,V> loHead = null, loTail = null;
- // 高位头 高位尾
- Node<K,V> hiHead = null, hiTail = null;
- //下个节点
- Node<K,V> next;
- //循环遍历
- do {
- //取出next节点
- next = e.next;
- //通过 与操作 计算得出结果为0
- if ((e.hash & oldCap) == 0) {
- //如果低位尾为null,证明当前数组位置为空,没有任何数据
- if (loTail == null)
- //将e值放入低位头
- loHead = e;
- //低位尾不为null,证明已经有数据了
- else
- //将数据放入next节点
- loTail.next = e;
- //记录低位尾数据
- loTail = e;
- }
- //通过 与操作 计算得出结果不为0
- else {
- //如果高位尾为null,证明当前数组位置为空,没有任何数据
- if (hiTail == null)
- //将e值放入高位头
- hiHead = e;
- //高位尾不为null,证明已经有数据了
- else
- //将数据放入next节点
- hiTail.next = e;
- //记录高位尾数据
- hiTail = e;
- }
-
- }
- //如果e不为空,证明没有到链表尾部,继续执行循环
- while ((e = next) != null);
- //低位尾如果记录的有数据,是链表
- if (loTail != null) {
- //将下一个元素置空
- loTail.next = null;
- //将低位头放入新数组的原下标位置
- newTab[j] = loHead;
- }
- //高位尾如果记录的有数据,是链表
- if (hiTail != null) {
- //将下一个元素置空
- hiTail.next = null;
- //将高位头放入新数组的(原下标+原数组容量)位置
- newTab[j + oldCap] = hiHead;
- }
- }
- }
- }
- }
- //返回新的数组对象
- return newTab;
- }
Cuatro: resumen
HashMap es una de las estructuras de datos más utilizadas en Java y se utiliza para almacenar pares clave-valor. El siguiente es un resumen de varios escenarios de uso comunes de HashMap:
1. Gestión de caché: HashMap se puede utilizar para implementar la función de caché y almacenar datos en HashMap en forma de pares clave-valor. Puede obtener los datos requeridos consultando HashMap, evitando el costo de recalcular o consultar la base de datos.
2. Índice de datos: HashMap es una estructura de datos que puede encontrar datos rápidamente. Puede encontrar rápidamente el valor correspondiente según la clave. Por lo tanto, HashMap se puede utilizar para crear una estructura de índice y mejorar la eficiencia de la recuperación de datos.
3. Diccionario: HashMap se puede utilizar para implementar la función de diccionario, almacenando palabras y significados correspondientes como pares clave-valor en HashMap. Obtenga el significado correspondiente a través de la clave de consulta para lograr una búsqueda rápida.
4. Estadísticas de frecuencia: HashMap se puede utilizar para contar la frecuencia de aparición de cada elemento en los datos. Puede utilizar el elemento como clave y el número de apariciones como valor, y obtener el elemento con la frecuencia más alta ordenando o consultando los valores.
5. Almacenamiento y recuperación de datos: HashMap es una estructura de datos eficiente que se puede utilizar para almacenar y recuperar grandes cantidades de datos. El valor correspondiente se puede encontrar rápidamente según la clave, lo que mejora la eficiencia del acceso a los datos.
En resumen, HashMap puede desempeñar un papel en escenarios donde es necesario almacenar y recuperar datos y, debido a su método de acceso eficiente, es una buena opción en la mayoría de los casos.