什么是拉链法?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/abm1993/article/details/80886058

一 介绍

    拉链法又叫链地址法,Java中的HashMap在存储数据的时候就是用的拉链法来实现的,拉链发就是把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中。T中各分量的初值应为空指针(来自百度知道)。

二 具体实现(依据HashMap分析)

//当我们向HashMap中进行存储数据对象时,我们先根据key中,生成一个hash值
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    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) /*判断当前的HashMap是否已经初始化,
这里的table,是Node<K,V>[] table数组,而Node就是一个链表实现*/
            n = (tab = resize()).length;//进行初始化
        if ((p = tab[i = (n - 1) & hash]) == null)//位运算获取key对应的数组的索引,并检查对应的数据是否存在
            tab[i] = newNode(hash, key, value, null);//当table数组对应没有结点数据时,创建新的结点,此时结点的next为null(构造函数最后一个参数),插入的数据便是链表头部。
        else {//这里说明对应节点已经至少存在一个结点数据
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))//判断当前的结点是否与插入的结点为同一个节点
                e = p;//如果是看后续分析
            else if (p instanceof TreeNode)//进入红黑树逻辑,暂不重点介绍
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {//循环遍历链表数据
                    if ((e = p.next) == null) {//链表尾部,往链表插入
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) /* -1 for 1st//jdk1.8之后为了弥补之前数组+链表的实现,
                                                             在链表有n个元素时,遍历的时间复杂度O(n),
                                                           引入了红黑树(复杂度为O(logn),这里暂不细分,只着重分析拉链法的具体实现*/
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;//循环寻找链表中是否存在同key的情况,存在则进入后续分析
                    p = e;//既没有达到链表尾部,又没有相同key,继续遍历链表,直到链表尾部
                }
            }
            if (e != null) { // existing mapping for key//后续分析
                V oldValue = e.value;//如果进入此判断,说明当前的key值存在值,则更新value值
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

上诉HashMap的添加数据的代码逻辑,淋漓尽致的体现了拉链法的代码实现,简单来说拉链发就是数组加链表

    //通过指定的key向HashMap中获取对应的value
    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

    /**
     * Implements Map.get and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {//
            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;//满足条件:HashMap还未初始化,已初始化但length为0,未在HashMap中找到指定key对应的value时返回null
    }

三 总结
从HashMap的实现来看,我们总结拉链发的实现步骤如下:
1. 计算 key 的 hashValue
2. 根据 hashValue 值定位到 table[hashIndex] 。( table[hashIndex] 是一条链表Node)
3. 若 table[hashValue] 为空则直接插入,不然则添加到链表末尾

猜你喜欢

转载自blog.csdn.net/abm1993/article/details/80886058