手写HashMap 手撕哈希表

下面编写的HashMap与JDK1.8源码的hHashMap来说:

忽略了链表转化为红黑树的情况。

public class HashMap<K,V> implements Map<K,V>{
    //是所有节点数量总大小
    private int size ;
    private static final boolean RED = false ;
    private static final boolean BLACK = true ;
    //table数组存储每一个插入的红黑树的根节点Node<k,V>
    private Node<K,V>[] table ;
    //table数组一开始默认给一个初始长度(哈希表数组最好长度为 2的n次幂)
    private static final int DEFAULT_CAPACITY = 1 << 4 ;

    public HashMap() {
        table = new Node[DEFAULT_CAPACITY] ;
    }
    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0 ;
    }

    @Override
    public void clear() {
        if (size == 0) {
            return;//如果哈希表没有Node节点 那么直接返回
        }
        for(int i = 0 ; i<table.length ;i++) {
            table[i] = null ;
        }
        size = 0 ;
    }

    /**
     * 添加操作
     * @param key
     * @param value
     * @return 返回值为被覆盖的value值
     */
    @Override
    public V put(K key, V value) {
        resize() ;
        int index = index(key) ;
        //取出index 位置的红黑树根节点
        Node<K,V> root = table[index] ;
        boolean searched = false ;
        if (root == null) {
            root = new Node(key,value, root.hash, null) ;
            table[index] = root ;
            size ++ ;
            afterPut(root);
            return null ;
        }
        //2.说明添加的不是树中第一个元素
        //2.1 确定根节点 root
        Node<K,V> node = root ;
        //2.2 假设出待插入节点对应的父节点的值
        Node<K,V> parent = null ;
        //2.3 记录最后一次比较器返回的值 来确定待插入节点插入到哪个方向
        int cmp = 0 ;
        K k1 = key ;
        int h1 = key == null ? 0 : key.hashCode() ;//找出传入的key对应的hash值
        //2.4 循环寻找待插入节点需要插入到的位置
        do {
            //记录cmp
            cmp = compare(node.key,key,h1,node.hash) ;
            //记录parent
            parent = node ;
            K k2 = node.key ;
            int h2 = node.hash ;
            Node<K,V> result = null ;
            //和node方法思路基本一致 所以详细注释见node方法
            if (h1 > h2) {
                cmp = 1 ;
            } else if (h1 < h2) {
                cmp = -1 ;
            } else if (Objects.equals(k1,k2)) {//已经判断过内存地址相同的情况了
                cmp = 0 ;
            } else if (k1 != null && k2 != null
            && k1.getClass() == k2.getClass()
            && k1 instanceof Comparable
            && (cmp = ((Comparable)k1).compareTo(k2))!=0) {
                //排除compareTo方法返回结果为0的情况 因为为0也并不代表两个对象是相同的

            } else if (!searched) {//searched = false
                //先扫描红黑树 如果不存在待添加的key 那么再进行使用内存地址比较!
                if ((node.left != null && (result = node(node.left,k1))!=null)
                ||(node.right != null && (result = node(node.right,k1))!=null)) {
                    //说明已经存在待添加的key,此时result为待添加的节点在红黑树中所找到的节点 。node为根节点
                    //要新赋值node节点 重复利用之后的代码
                    node = result ;
                    cmp = 0 ;
                } else { //不存在这个key
                    searched = true ;
                    //说明不存在待添加节点对应的key值 那么只可以使用内存地址进行比较添加
                    cmp = System.identityHashCode(k1) - System.identityHashCode(k2) ;
                }
            } else {
                //searched = true
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2) ;
            }
            if (cmp > 0) {
                //说明已存在节点元素值大,那么插到左边
                node = node.right ;
            } else if (cmp < 0) {
                node = node.left ;
            } else {
                //cmp == 0 如果自定义比较器比较出相等时,我们这里的处理方式是覆盖值操作
                node.key = key ;
                V oldValue = node.value ;
                node.value = value ;
                return oldValue ;//返回的是被覆盖的value值
            }
        } while (node != null) ;
        //3.退出循环之后,根据记录的parent值 进行创建待插入节点对应的Node对象
        Node<K,V> newNode = new Node<>(key,value,h1,parent) ;
        //4.根据记录的cmp值和parent值,进行确定把待插入节点插入到哪一个位置
        if (cmp > 0) {
            parent.left = newNode ;
        } else {
            //cmp < 0 的情况,cmp == 0 直接就返回了已经
            parent.right = newNode ;
        }
        size ++ ;
        //添加node节点后 进行的调整
        afterPut(node); ;
        return null ;
    }

    /**
     * 获取对应key的value
     * @param key 传入的key键值
     * @return 返回key对应的value值
     */
    @Override
    public V get(K key) {
        Node<K,V> node = node(key) ;
        return node != null ? node.value : null ;
    }

    @Override
    public V remove(K key) {
        return remove(node(key));
    }

    @Override
    public boolean containsKey(K key) {
        return node(key) != null ;
    }

    /**
     * 对于value的值不可以直接查询到 需要遍历所有节点
     * @param value
     * @return
     */
    @Override
    public boolean containsValue(V value) {
        if (size == 0) {//如果没有节点 那么返回false
            return false ;
        }
        Queue<Node<K,V>> queue = new LinkedList<>() ;
        //这里的层序遍历和之前不同 需要遍历多个红黑树 所以使用for循环
        for (int i = 0 ;i< table.length ;i++) {
            if (table[i] == null) {
                return false ;
            }
            queue.offer(table[i]) ;
            while (!queue.isEmpty()) {
                Node<K,V> node = queue.poll() ;
                if (Objects.equals(value,node.value)) {
                    return true ;
                }
                if (node.left != null) {
                    queue.offer(node.left) ;
                }
                if (node.left != null) {
                    queue.offer(node.right) ;
                }
            }
        }
        return false;
    }

    @Override
    public void traversal(Visitor<K, V> visitor) {
        if (size == 0 || visitor == null) {
            return;
        }
        Queue<Node<K,V>> queue = new LinkedList<>() ;
        for (int i = 0;i < table.length ;i++) {
            if (table[i] == null) {
                return;
            }
            queue.offer(table[i]) ;
            while (!queue.isEmpty()) {
                Node<K,V> node = queue.poll() ;
                if (visitor.visit(node.key,node.value)) {
                    return;
                }
                if (node.left != null) {
                    queue.offer(node.left) ;
                }
                if (node.right != null) {
                    queue.offer(node.right) ;
                }
            }
        }
    }

    private void resize() {
        //说明装填因子 <= 0.75 无需扩容 直接返回
        if (size / table.length <= DEFAULT_CAPACITY) {
            return;
        }
        // >0.75 扩容到原来的两倍
        Node<K,V> []oldTable = table ;
        table = new Node[oldTable.length >> 1] ;
        Queue<Node<K,V>> queue = new LinkedList<>() ;
        //把原来table数组上的key-value键值对每一个节点都进行转移到新数组
        /**
         * 当扩容为原来容量的2倍时,节点的索引有2种情况:
         * 1.保持不变
         * 2.要么就是index = index + 旧容量
         */
        for (int i = 0 ; i< oldTable.length ;i++) {
            if (oldTable[i] == null) {
                return;
            }
            queue.offer(oldTable[i]) ;
            while (!queue.isEmpty()) {
                Node<K,V> node = queue.poll() ;
                if (node.left != null) {
                    queue.offer(node.left) ;
                }
                if (node.right != null) {
                    queue.offer(node.right) ;
                }
                moveNode(node) ;//移动遍历到旧数组的每一个元素
            }
        }
    }
    private void moveNode(Node<K,V> newNode) {
        //重置清空所有节点的父子关系以及左右关系
        newNode.parent = null ;
        newNode.left = null ;
        newNode.right = null ;

        int index = index(newNode.key) ;
        //取出index 位置的红黑树根节点
        Node<K,V> root = table[index] ;
        if (root == null) {//说明该桶索引位置还没有节点插入
            root = newNode ;//所以直接插给root
            table[index] = root ;
            afterPut(root);
            return;
        }
        //2.说明添加的不是树中第一个元素
        //2.1 确定根节点 root
        Node<K,V> node = root ;
        //2.2 假设出待插入节点对应的父节点的值
        Node<K,V> parent = null ;
        //2.3 记录最后一次比较器返回的值 来确定待插入节点插入到哪个方向
        int cmp = 0 ;
        K k1 = newNode.key ;
        int h1 = newNode.hash ;//找出传入的key对应的hash值
        //2.4 循环寻找待插入节点需要插入到的位置
        do {
            //记录parent
            parent = node ;
            K k2 = node.key ;
            int h2 = node.hash ;
            Node<K,V> result = null ;
            //和node方法思路基本一致 所以详细注释见node方法
            if (h1 > h2) {
                cmp = 1 ;
            } else if (h1 < h2) {
                cmp = -1 ;
            } else if (k1 != null && k2 != null
            && k1 instanceof Comparable
            && k1.getClass() == k2.getClass()
            && (cmp = ((Comparable) k1).compareTo(k2))!=0) {

            } else {
                //searched = true
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2) ;
            }
            if (cmp > 0) {
                //说明已存在节点元素值大,那么插到左边
                node = node.right ;
            } else if (cmp < 0) {
                node = node.left ;
            }
        } while (node != null) ;
        //4.根据记录的cmp值和parent值,进行确定把待插入节点插入到哪一个位置
        if (cmp > 0) {
            parent.left = newNode ;
        } else {
            //cmp < 0 的情况,cmp == 0 直接就返回了已经
            parent.right = newNode ;
        }
        newNode.parent = parent ;
        //添加node节点后 进行的调整
        afterPut(node);
    }
    private V remove(Node<K,V> node) {
        if (node == null) {
            return null;
        }
        V oldValue = node.value ;
        size -- ;
        //(1)删除的是度为2的节点
        if (node.hasTwoChildren()) {
            //找到该节点对应的前驱或后继节点
            Node<K,V> s = succeed(node) ;//找到后继节点
            //使用后继节点的值进行覆盖度为2的节点的值
            node.key = s.key ;
            node.value = s.value ;
            //把待删除的节点进行赋值为前驱或后继节点,使用后面的逻辑进行删除前驱后继节点
            node = s ;
        }
        //必须搞出一个replacement进行记录删除的是根节点左子树还是右子树的节点
        Node<K,V> replacement = node.left != null ? node.left : node.right ;
        int index = index(node.key) ;
        //(2)删除的是度为1的节点
        if (replacement != null) {
            //统一进行更新删除节点度为1的操作之后的parent指向值
            replacement.parent = node.parent ;
            //2.1 如果是度为1的节点并且是root根节点
            if (node.parent == null) {
                table[index] = replacement ;
            } else if (node == node.parent.left) {
                //2.2 如果是左子树上的度为1的节点
                node.parent.left = replacement ;
            } else if (node == node.parent.right) {
                //2.3 如果是右子树上的度为1的节点
                node.parent.right = replacement ;
            }
            //真正被删除的是度为1或2的前驱或后继节点
            afterRemove(node,replacement) ;
        } else if (node.parent == null) {// (3) 如果删除的是叶子节点并且为root根节点
            table[index] = null ;
        } else {//(4) 如果删除的是叶子节点但是不为root根节点
            if (node == node.parent.right) {
                //4.1 如果是左子树上的度为0的节点
                node.parent.right = null ;
            } else {//node = node.parent.left
                //4.2 如果是右子树上的度为0的节点
                node.parent.left = null ;
            }
        }
        return oldValue ;
    }
    /**
     * 找到后继节点
     * @param node
     * @return
     */
    private Node<K,V> succeed(Node<K,V> node) {
        if (node == null) {
            return null ;
        }
        //1,
        Node<K,V> p = node ;
        if (p.right != null) {
            p = p.right ;
            while (p.left != null) {
                p = p.left ;
            }
            return p ;
        }
        //p.right == null
        //目的是找node节点的后继节点,所以要找第一个比它大的
        //所以在父节点不为空 并且一直在变化的父节点的右子树 、
        // 如果它发现在左子树后,那么就要退出循环,说明找到了第一个比它大的节点
        if (p.parent != null && p == p.parent.right) {
            p = p.parent ;
        }
        //p.parent == null
        //p == p.parent.left
        return p.parent ;
    }
    /**
     * HashMap中的key是不一定 也不太可能具备可比较性的
     * 所以使用hash值去进行决定出加到哪个位置
     * @param k1 键值1
     * @param k2 键值2
     * @param h1 k1对应的hash值
     * @param h2 k2对应的hash值
     * @return
     */
    private int compare(K k1,K k2,int h1,int h2)  {
        //比较哈希值
        int result = h1 - h2 ;
        if (result != 0) {
            return result ;
        }
        // result == 0 说明哈希值相等 但是还要进行比较是否equals
        boolean equals = Objects.equals(k1, k2);//已经判断出k1==null && k2 == null的情况了。
        if (equals) {
            //说明k1,k2equals
            return 0 ;
        }
        //说明只是哈希值相等 但是不equals
        //但是总要分辨出谁大谁小 所以比较类名
        if (k1 != null && k2 != null) {
            String k1Cls = k1.getClass().getName() ;
            String k2Cls = k2.getClass().getName() ;
            result = k1Cls.compareTo(k2Cls) ;
            if (result != 0) {
                return result ;
            }
            //result == 0 说明是同一种类型并且具有可比较性
            if (k1 instanceof Comparable) {
                return ((Comparable)k1).compareTo(k2) ;
            }
        }
        //说明是同一种类型 哈希值相等 但是不具备可比较性
        //k1 != null && k2 == null
        //k1 == null && k2 != null
        //此时只可以进行两个key键值内存地址的比较
        return System.identityHashCode(k1) - System.identityHashCode(k2) ;
    }

    private void afterRemove(Node<K,V> node, Node<K,V> replacement) {
        //1。如果删除的节点是红色RED,删除之后无需处理
        if (isRed(node)) return;
        //2.用以取代被删除的node的子节点是RED时
        if (isRed(replacement)) {
            black(replacement) ;
            return;
        }
        Node<K,V> parent = node.parent ;
        //删除的是根节点
        if (parent == null) return;
        //3.删除的是黑色叶子节点
        //判断被删除的node是左还有右:
        //parent.left == null说明被删除的node节点是parent的左子树,
        // 那么被删除的node节点的兄弟节点即是parent的右子树,反之同理即可
        boolean left = parent.left == null ;
        Node<K,V> sibling = left ? parent.right : parent.left ;
        if (left) {//3.1被删除的节点在左边,那么兄弟节点sibling在右边
            if (isRed(sibling)) {
                //3.2.1删除的是黑色叶子节点并且sibling是红色,那么转换为sibling为黑色
                black(sibling) ;
                red(parent) ;
                rotateRight(parent) ;
                //更换兄弟
                sibling = parent.right ;
            }
            //3.2.2删除的是黑色叶子节点并且sibling为BLACK
            //3.2.2.1 兄弟节点没有一个是红色子节点,父节点要下根兄弟节点合并
            if (isBlack(sibling.right) && isBlack(sibling.left)) {
                //记录一下是否父节点也会下溢
                boolean parentBlack = isBlack(parent) ;
                black(parent) ;
                red(sibling) ;
                if (parentBlack) {
                    afterRemove(parent,null) ;
                }
            } else {  //3.2.2.2 兄弟节点至少有1个红色子节点,被删除节点向兄弟节点借用元素
                //兄弟节点的左边是黑色,说明右边是红色子节点
                if (isBlack(sibling.right)) {
                    rotateLeft(sibling) ;
                    sibling = parent.right ;//旋转后要更新兄弟节点
                    color(sibling,colorOf(parent)) ;
                    black(sibling.right) ;
                    black(parent) ;
                }
                //兄弟节点的左边是红色,说明左边是红色子节点
                color(sibling,colorOf(parent)) ;
                black(sibling.right) ;
                black(parent) ;
                rotateRight(parent);
            }
        } else {//3.2被删除的节点在右边,那么兄弟节点sibling在左边
            if (isRed(sibling)) {
                //3.2.1删除的是黑色叶子节点并且sibling是红色,那么转换为sibling为黑色
                black(sibling) ;
                red(parent) ;
                rotateRight(parent) ;
                //更换兄弟
                sibling = parent.left ;
            }
            //3.2.2删除的是黑色叶子节点并且sibling为BLACK
            //3.2.2.1 兄弟节点没有一个是红色子节点,父节点要下根兄弟节点合并
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                //记录一下是否父节点也会下溢
                boolean parentBlack = isBlack(parent) ;
                black(parent) ;
                red(sibling) ;
                if (parentBlack) {
                    afterRemove(parent,null) ;
                }
            } else {  //3.2.2.2 兄弟节点至少有1个红色子节点,被删除节点向兄弟节点借用元素
                //兄弟节点的左边是黑色,说明右边是红色子节点
                if (isBlack(sibling.left)) {
                    rotateLeft(sibling) ;
                    sibling = parent.left ;//旋转后要更新兄弟节点
                    color(sibling,colorOf(parent)) ;
                    black(sibling.left) ;
                    black(parent) ;
                }
                //兄弟节点的左边是红色,说明左边是红色子节点
                color(sibling,colorOf(parent)) ;
                black(sibling.left) ;
                black(parent) ;
                rotateRight(parent);
            }
        }
    }
    /**
     * 添加操作可能毁坏红黑树的性质
     * afterPut修复红黑树的性质
     * @param node
     */
    private void afterPut(Node<K, V> node) {
        Node<K,V> parent = node.parent ;
        //1.情况一中的四种添加位置,是无需进行修复处理红黑树的。
        //1.1添加的是根节点
        if (parent == null) {
            black(node) ;//根节点直接染成黑色
            return;
        }
        //1.2如果父节点是黑色,直接返回
        if (isBlack(parent)) return ;
        //2.情况二中的八种添加位置,是需要进行修复处理红黑树的。
        //2.1拿到uncle节点进行判断 uncle节点:叔父节点即是父节点的兄弟节点
        Node<K,V> uncle = parent.sibling() ;
        //2.2祖父节点
        Node<K,V> grand = parent.parent ;
        //2.3 叔父节点为RED,只需进行染色 然后进行递归grand作为新添加的节点进行处理
        if (isRed(uncle)) {
            black(parent) ;
            black(uncle) ;
            //把祖父节点当作是新添加的节点进行处理
            red(grand) ;
            afterPut(grand); ;
            return;
        }
        //2.4 叔父节点不为RED,需要进行旋转处理
        if (parent.isLeftChild()) { //parent是grand的左子树节点 L
            if (node.isLeftChild()) { //node是parent的左子树节点 LL
                //LL和RR为一类,parent染成BLack,grand染成RED
                black(parent) ;
                red(grand) ;
                rotateRight(grand);
            } else { //LR
                //LR 和 RL为一类,新添加的节点染成Black,grand染成RED
                black(node) ;
                red(grand) ;
                rotateLeft(parent) ;
                rotateRight(grand) ;
            }
        } else { //R
            if (node.isLeftChild()) { //RL
                //LR 和 RL为一类,新添加的节点染成Black,grand染成RED
                black(node) ;
                red(grand) ;
                rotateRight(parent); ;
                rotateLeft(grand); ;
            } else {//RR
                //LL和RR为一类,parent染成BLack,grand染成RED
                black(parent) ;
                red(grand) ;
                rotateLeft(grand);
            }
        }
    }

    /**
     * 对传入的节点进行染色处理
     * @param node 想要染色的节点
     * @param color 把该节点node染成color颜色
     * @return 返回染色后的节点
     */
    private Node<K,V> color(Node<K,V> node, boolean color) {
        if (node == null) {
            return node ;
        }
        node.color = color ;
        return node ;
    }

    /**
     * 把该node节点染成RED
     */
    private Node<K,V> red(Node<K,V> node) {
        return color(node,RED) ;
    }

    /**
     * 把该node节点染成BLACK
     */
    private Node<K,V> black(Node<K,V> node) {
        return color(node,BLACK) ;
    }

    /**
     * 传入一个node节点,进行返回该节点是什么颜色
     */
    private boolean colorOf(Node<K,V> node) {
        //如果node为null,即是null节点 即是BLACK
        // 【对null节点进行特殊处理的原因是:我们一开始并没有对null节点的颜色进行设置】
        //如果不为空,那么返回node实际的颜色即可
        return node == null ? BLACK : node.color ;
    }

    /**
     * 判断该node节点颜色color是否为BLACK
     */
    private boolean isBlack(Node<K,V> node) {
        return colorOf(node) == BLACK ;
    }

    /**
     * 判断该node节点颜色color是否为RED
     */
    private boolean isRed(Node<K,V> node) {
        return colorOf(node) == RED ;
    }

    /**
     * 左旋转
     * @param grand 想要进行旋转的节点,传过来的是什么,什么节点就进行左旋转
     */
    private void rotateLeft(Node<K,V> grand) {
        //进行左旋转,说明是RR,可以确定parent
        Node<K,V> parent = grand.right ;
        //child即是T1
        Node<K,V> child = parent.left ;
        grand.right =  child; //1.
        parent.left = grand ; //2.
        //执行旋转之后的逻辑
        afterRotate(grand,parent,child);
    }
    /**
     * 右旋转
     * @param grand 想要进行旋转的节点,传过来的是什么,什么节点就进行右旋转
     */
    private void rotateRight(Node<K,V> grand) {
        //1.进行右旋转,说明是LL,那么可以确定parent
        Node<K,V> parent = grand.left ;
        //2.找出T2
        Node<K,V> child = parent.right ;
        //3.对g执行右旋转
        grand.left = child ; //3.1
        parent.right = grand ;//3.2
        //执行旋转之后的逻辑
        afterRotate(grand,parent,child);
    }
    /**
     * 旋转之后统一要做的逻辑
     * @param grand (最低的失去平衡的节点)
     * @param parent (parent是grand的子节点)
     * @param child 旋转过程中父节点改变的节点(T1或T2)
     */
    private void afterRotate(Node<K,V> grand, Node<K,V> parent, Node<K,V> child) {
        int index = index(grand.key) ;
        //3.让parent成为这颗子树的根节点
        //3.1parent先连上grand的父节点
        parent.parent = grand.parent ;
        //3.2 grand再去连上parent
        if (grand.isLeftChild()) {
            //如果grand是父节点的左子树
            grand.parent.left = parent ;
        } else if (grand.isRightChild()) {
            //如果grand是父节点的右子树
            grand.parent.right = parent ;
        } else {//既不是父节点的左子树又不是右子树,那么说明grand即是root根节点
            table[index] = parent ;
        }
        //4.更新child(T1)的parent
        if (child != null) {
            child.parent = grand ;
        }
        //5.更新grand的parent
        grand.parent = parent ;
    }
    /**
     * 根据key找到Node节点
     */
    public Node<K,V> node(K key) {
        Node<K,V> root = table[index(key)] ;
        return root == null ? null : node(root,key) ;//递归调用
    }
    /**
     * 根据key找到Node节点
     */
    private Node<K,V> node(Node<K,V> node,K k1) {
        int index = index(k1) ;
        int h1 = k1 == null ? 0 : k1.hashCode() ;
        Node<K,V> result = null ;
        int cmp = 0 ;
        while (node != null) {
            K k2 = node.key ;
            int h2 = node.hash ;
            //先比较哈希值
            if (h1 > h2) {
                node = node.right ;
            } else if (h1 < h2) {
                node = node.left ;
            } else if (Objects.equals(k1,k2)) {
                //哈希值相等 并且equals相等 并且已经判断过内存地址相同的情况了 之后都是内存地址不相等的情况
                return node ;//那么找到了
            } else if (k1 != null && k2 != null
                    && k1.getClass() == k2.getClass()
                    && k1 instanceof Comparable
                    && (cmp = ((Comparable)k1).compareTo(k2))!=0) { //哈希值相等 但是equals不相等 但是类相同 并且具备可比较性
                if (cmp > 0) {
                    node = node.right ;
                } else if (cmp < 0) {
                    node = node.left;
                }
//                } else { //cmp == 0 并不代表两个对象真的就是相等的,所以要执行后面遍历搜索的代码
//                    return node ;
//                }
            } else if (node.right != null && (result = node(node.right,k1))!=null) {
                //哈希值相等 但是不具备可比较性 也不equals。之后进行递归 如果向右可以找到的话
                return result;
//            } else if (node.left != null && (result = node(node.left,k1)) !=null) {
//                //哈希值相等 但是不具备可比较性 也不equals。之后进行递归 如果向左可以找到的话
//                return result ;
//            } else {//找不到
//                return null ;
//            }
            } else {//只能往左边,减少递归
                node = node.left ;
            }
        }
        return null ;//找不到
    }
    /**
     * 根据key生成对应的索引(在桶数组的位置)
     */
    private int index(K key) {
        //1.使用自身定义的hashCode方法获取哈希值
        int hash = key.hashCode();
        //2.自身hashCode方法或许获取的哈希值是不稳定的 所以jdk再进行帮忙均匀计算一下
        hash = hash ^ (hash >>> 16) ;
        //3.计算出索引值并且返回
        return hash & (table.length - 1) ;
    }
    private static class Node<K,V> {
        K key ;
        V value ;
        boolean color = RED;
        int hash ;
        Node<K,V> left ;//左子节点
        Node<K,V> right ;//右子节点
        Node<K,V> parent ;//父节点
        int height ;
        public Node(K key, V value , int hash, Node<K,V> parent) {
            this.key = key ;
            this.value = value ;
            this.hash = key == null ? 0 : key.hashCode() ;
            this.parent = parent ;//除了根节点,其余节点都具有父节点,所以要在构造方法传入
        }
        //判断是否为叶子节点
        public boolean isLeaf() {
            return left == null && right == null ;
        }
        //判断是否具有两个子节点
        public boolean hasTwoChildren() {
            return left != null && right != null ;
        }
        //说明是parent的左子树
        public boolean isLeftChild() {
            return parent != null && this == parent.left ;
        }
        //说明是parent的右子树
        public boolean isRightChild() {
            return parent != null && this == parent.right ;
        }
        /**
         * 找出兄弟节点
         * @return
         */
        public Node<K,V> sibling() {
            if (isLeftChild()) {
                //说明该节点是父节点的左子树
                return parent.right ;//说明该节点的兄弟节点即是父节点的右子树
            }
            if (isRightChild()) {
                //说明该节点是父节点的右子树
                return parent.left ;//说明该节点的兄弟节点即是父节点的左子树
            }
            //说明该节点既不是父节点的左子树 也不是父节点的右子树
            //那么说明该节点没有父节点,那么就没有兄弟节点
            return null ;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值