【恋上数据结构与算法 第一季】哈希表(散列表)


持续学习&持续更新中…


TreeMap分析

在这里插入图片描述

需求

在这里插入图片描述

哈希表

在这里插入图片描述

table数组

HashMap中的table数组是一个Node<K,V>数组(也就是红黑树数组),里面放每一颗红黑树的根节点,也就是说,table数组里面会有很多颗红黑树。

在这里插入图片描述

哈希冲突

在这里插入图片描述

JDK1.8解决哈希冲突的方法

在这里插入图片描述

哈希函数

index = hash(key)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如何生成key的哈希值(hashCode)

如何生成key的hashCode

在这里插入图片描述

已知:Java中的hashCode返回值必须为int型

在这里插入图片描述

Integer和Float

在这里插入图片描述

在这里插入图片描述

Long和Double

在这里插入图片描述

在这里插入图片描述

字符串

公式:之前的hashCode值 * n + 当前的hashCode值

在这里插入图片描述

在这里插入图片描述

自定义对象

  • 实现hashCode是为了自定义哈希函数如何生成哈希表的索引
  • 实现equals是为了自定义节点比较时(索引相同时,也就是哈希冲突时,会从链表or红黑树的头遍历到尾来比较节点的key是否相同)如何是相同的节点如何是不同的节点
  • 当两个对象equals时,就代表这两个对象是同一个对象,既然是同一个对象,那么它们的hashCode值必须相等才对,因此hashCode和equals都必须同时实现(equals中使用到了哪些属性来比较两个对象相等,那么hashCode的值也应该使用这些属性来计算)。

在这里插入图片描述

在这里插入图片描述

pub与findNode

简化版

  • 只比较equals
  • 如果不equals,直接扫描
  • 性能特别差

put

private V put(Node<K, V> root, K key, V value) {
    
    
        Node<K, V> node = root;
        Node<K, V> parent = root;
        Node<K, V> result;
        int h1 = (key == null ? 0 : key.hashCode());
        K k1 = key;
        int cmp;
        boolean searched = false;
        do {
    
    
            parent = node;
            int h2 = node.hashCode;
            K k2 = node.key;
            if (Objects.equals(k1, k2)) cmp = 0;
            else if (searched) {
    
     // 已经搜索过
                cmp = 1; // 直接往右走
            } else {
    
    
                if (
                        (node.left != null && (result = findNode(node.left, k1)) != null) ||
                                (node.right != null && (result = findNode(node.right, k1)) != null)
                ) {
    
     // 找到了
                    cmp = 0;
                    node = result;
                } else {
    
     // 不存在,没找到
                    searched = true;
                    cmp = 1; // 同样直接往右走
                }
            }

            if (cmp > 0) {
    
    
                node = node.right;
            } else if (cmp < 0) {
    
    
                node = node.left;
            } else {
    
    
                    /*
                        元素相等时建议更新元素
                        之前key存在时,更新key和value
                    */
                V oldValue = node.value;
                node.key = key;
                node.value = value;
                node.hashCode = h1;
                return oldValue;
            }
        } while (null != node);
        Node<K, V> newNode = new Node<>(key, value, parent);
        if (cmp > 0) {
    
    
            parent.right = newNode;
        } else {
    
     // cmp < 0
            parent.left = newNode;
        }
        size++;
        afterPut(newNode);
        return null;
    }

findNode

    private Node<K, V> findNode(Node<K, V> node, K key) {
    
    
        int h1 = (key == null ? 0 : key.hashCode());
        K k1 = key;
        Node<K, V> result;
        while (node != null) {
    
    
            int h2 = node.hashCode;
            K k2 = node.key;
            if (Objects.equals(k1, k2)) return node;
            else if (node.left != null && (result = findNode(node.left, k1)) != null) {
    
    
                return result;
            } else {
    
     // 左边找不到,只能往右边找
                node = node.right;
            }
        }
        return null;
    }

优化版

put

private V put(Node<K, V> root, K key, V value) {
    
    
        Node<K, V> node = root;
        Node<K, V> parent = root;
        Node<K, V> result;
        int h1 = (key == null ? 0 : key.hashCode());
        K k1 = key;
        int cmp;
        boolean searched = false;
        do {
    
    
            parent = node;
            int h2 = node.hashCode;
            K k2 = node.key;
            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
            ) {
    
    
            } else if (searched) {
    
     // 已经搜索过
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
            } else {
    
    
                if (
                        (node.left != null && (result = findNode(node.left, k1)) != null) ||
                                (node.right != null && (result = findNode(node.right, k1)) != null)
                ) {
    
     // 找到了
                    cmp = 0;
                    node = result;
                } else {
    
     // 不存在,没找到
                    searched = true;
                    cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
                }
            }
// 性能不好,应该使用searched优化            
//            else if (
//                    (node.left != null && (result = findNode(node.left, k1)) != null) ||
//                            (node.right != null && (result = findNode(node.right, k1)) != null)
//            ) {
    
    
//                cmp = 0;
//                node = result;
//            } else {
    
    
//                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
//            }

            if (cmp > 0) {
    
    
                node = node.right;
            } else if (cmp < 0) {
    
    
                node = node.left;
            } else {
    
    
                    /*
                        元素相等时建议更新元素
                        之前key存在时,更新key和value
                    */
                V oldValue = node.value;
                node.key = key;
                node.value = value;
                node.hashCode = h1;
                return oldValue;
            }
        } while (null != node);
        Node<K, V> newNode = new Node<>(key, value, parent);
        if (cmp > 0) {
    
    
            parent.right = newNode;
        } else {
    
     // cmp < 0
            parent.left = newNode;
        }
        size++;
        afterPut(newNode);
        return null;
    }

findNode

private Node<K, V> findNode(Node<K, V> node, K key) {
    
    
        int h1 = (key == null ? 0 : key.hashCode());
        K k1 = key;
        Node<K, V> result;
        int cmp;
        while (node != null) {
    
    
            int h2 = node.hashCode;
            K k2 = node.key;
            if (h1 > h2) {
    
    
                node = node.right;
            } else if (h1 < h2) {
    
    
                node = node.left;
            } else if (Objects.equals(k1, k2)) return node;
            else if (k1 != null && k2 != null
                    && k1.getClass() == k2.getClass() && k1 instanceof Comparable
                    && (cmp = ((Comparable) k1).compareTo(k2)) != 0
            ) {
    
    
                node = cmp > 0 ? node.right : node.left;
            } else if (node.left != null && (result = findNode(node.left, k1)) != null) {
    
    
                return result;
            } else {
    
     // 左边找不到,只能往右边找
                node = node.right;
            }
// 性能不好
//            else if (node.right != null && (result = findNode(node.right, k1)) != null) {
    
    
//                return result;
//            } else {
    
    
//                return null;
//            }
        }
        return null;
    }

注意点

  • compareTo只能用来比大小,是用来比较大小是否相等的,不能用来比较对象是否完全相等(相同)

  • equals是用来判断对象是否相等(相同)的

  • 应该先比较hashCode的值,根据hashCode的大小去判断往左走还是往右走,这样会排除很多节点。

  • 只有当hashCode值相等的时候才有必要去比较是否equals。

    1. 前提:同时规范实现了hashCode和equals
    2. 当两个元素equals时,它们的hashCode值一定是相等的
    3. 当两个元素的hashCode值相等,这两个元素不一定equals
  • put和findNode时,比较的规则越完善越多,该HashMap的效率越高。

  • 除了put时在走投无路的情况下根据内存地址大小决定往左还是往右走这一种情况,put和findNode的比较规则必须是对称的。

  • findNode查找元素时,当所有规则都不能判断出两个元素是否相等,也分不清是往左走还是往右走时,不能根据内存地址的大小来判断往左走还是往右走,只能去扫描。

扩容

装填因子

在这里插入图片描述

扩容后index

在这里插入图片描述

扩容后hash(索引、数组下标)会变 ,因此不能直接将整颗红黑树移动到新的table数组,而是需要将每个节点一个一个的移动到新数组中。

HashMap的实现

  • 该HashMap并没有和JDK那样使用哈希表 + 链表 + 红黑树

  • 而是只使用了哈希表 + 红黑树

// 支持null作为key
public class HashMap<K, V> implements Map<K, V> {
    
    
    private static final int DEFAULT_CAPACITY = 1 << 4;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    private int size;
    private Node<K, V>[] table;

    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;
        for (int i = 0; i < table.length; i++) {
    
    
            table[i] = null;
        }
        size = 0;
    }

    @Override
    public V put(K key, V value) {
    
    

        resize();

        int index = hash(key);
        Node<K, V> root = table[index];
        if (null == root) {
    
    
            root = new Node<>(key, value, null);
            table[index] = root;
            size++;
            afterPut(root);
            return null;
        }
        // 哈希冲突,向红黑树中添加元素
        return put(root, key, value);
    }

    @Override
    public V get(K key) {
    
    
        final Node<K, V> node = findNode(table[hash(key)], key);
        if (node == null) return null;
        return node.value;
    }

    @Override
    public V remove(K key) {
    
    
        final Node<K, V> node = findNode(table[hash(key)], key);
        if (node == null) return null;
        return remove(node);
    }

    @Override
    public boolean containsKey(K key) {
    
    
        return findNode(table[hash(key)], key) != null;
    }

    @Override
    public boolean containsValue(V value) {
    
    
        if (size == 0) return false;
        Queue<Node<K, V>> queue = new LinkedListQueue<>();
        for (int i = 0; i < table.length; i++) {
    
    
            Node<K, V> node = table[i];
            if (node == null) continue;
            queue.enQueue(node);
            do {
    
    
                node = queue.deQueue();
                if (Objects.equals(value, node.value)) return true;
                if (node.left != null) queue.enQueue(node.left);
                if (node.right != null) queue.enQueue(node.right);
            } while (!queue.isEmpty());
        }
        return false;
    }

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

// HashMap不需要compare函数
//    private int compare(K k1, K k2, int h1, int h2) {
    
    
//        // 当两个对象equals时,就认定这两个对象是同一个对象,那么它们的hashCode值应该相等才对
//        // 但是当两个对象的hashCode值相等时,这两个对象不一定是equals的
//        int result = h1 - h2;
//        if (result != 0) return result;
//        if (Objects.equals(k1, k2)) return 0;
//        if (null != k1 && null != k2 && k1.getClass() == k2.getClass() && k1 instanceof Comparable) {
    
    
//            return ((Comparable) k1).compareTo(k2);
//        }
//        return System.identityHashCode(k1) - System.identityHashCode(k2);
//    }

    private V put(Node<K, V> root, K key, V value) {
    
    
        Node<K, V> node = root;
        Node<K, V> parent = root;
        Node<K, V> result;
        int h1 = (key == null ? 0 : key.hashCode());
        K k1 = key;
        int cmp;
        boolean searched = false;
        do {
    
    
            parent = node;
            int h2 = node.hashCode;
            K k2 = node.key;
            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
            ) {
    
    
            } else if (searched) {
    
     // 已经搜索过
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
            } else {
    
    
                if (
                        (node.left != null && (result = findNode(node.left, k1)) != null) ||
                                (node.right != null && (result = findNode(node.right, k1)) != null)
                ) {
    
     // 找到了
                    cmp = 0;
                    node = result;
                } else {
    
     // 不存在,没找到
                    searched = true;
                    cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
                }
            }
//            性能不好,应该使用searched优化
//            else if (
//                    (node.left != null && (result = findNode(node.left, k1)) != null) ||
//                            (node.right != null && (result = findNode(node.right, k1)) != null)
//            ) {
    
    
//                cmp = 0;
//                node = result;
//            } else {
    
    
//                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
//            }

            if (cmp > 0) {
    
    
                node = node.right;
            } else if (cmp < 0) {
    
    
                node = node.left;
            } else {
    
    
                    /*
                        元素相等时建议更新元素
                        之前key存在时,更新key和value
                    */
                V oldValue = node.value;
                node.key = key;
                node.value = value;
                node.hashCode = h1;
                return oldValue;
            }
        } while (null != node);
        Node<K, V> newNode = new Node<>(key, value, parent);
        if (cmp > 0) {
    
    
            parent.right = newNode;
        } else {
    
     // cmp < 0
            parent.left = newNode;
        }
        size++;
        afterPut(newNode);
        return null;
    }

    private Node<K, V> findNode(Node<K, V> node, K key) {
    
    
        int h1 = (key == null ? 0 : key.hashCode());
        K k1 = key;
        Node<K, V> result;
        int cmp;
        while (node != null) {
    
    
            int h2 = node.hashCode;
            K k2 = node.key;
            if (h1 > h2) {
    
    
                node = node.right;
            } else if (h1 < h2) {
    
    
                node = node.left;
            } else if (Objects.equals(k1, k2)) return node;
            else if (k1 != null && k2 != null
                    && k1.getClass() == k2.getClass() && k1 instanceof Comparable
                    && (cmp = ((Comparable) k1).compareTo(k2)) != 0
            ) {
    
    
                node = cmp > 0 ? node.right : node.left;
            } else if (node.left != null && (result = findNode(node.left, k1)) != null) {
    
    
                return result;
            } else {
    
     // 左边找不到,只能往右边找
                node = node.right;
            }
// 性能不好
//            else if (node.right != null && (result = findNode(node.right, k1)) != null) {
    
    
//                return result;
//            } else {
    
    
//                return null;
//            }
        }
        return null;
    }

    // 扩容
    private void resize() {
    
    
        if (size / ((float) table.length) <= DEFAULT_LOAD_FACTOR) return;

        Node<K, V>[] oldTable = table;
        table = new Node[oldTable.length << 1];
        Queue<Node<K, V>> queue = new LinkedListQueue<>();
        for (int i = 0; i < oldTable.length; i++) {
    
    
            Node<K, V> root = oldTable[i];
            if (root == null) continue;
            queue.enQueue(root);
            do {
    
    
                Node<K, V> node = queue.deQueue();
                if (node.left != null) queue.enQueue(node.left);
                if (node.right != null) queue.enQueue(node.right);
                move(node);
            } while (!queue.isEmpty());
        }
    }

    // 辅助扩容
    private void move(Node<K, V> newNode) {
    
    
        newNode.color = Node.RED;
        newNode.parent = null;
        newNode.left = null;
        newNode.right = null;

        int index = hash(newNode);
        Node<K, V> root = table[index];
        if (null == root) {
    
    
            root = newNode;
            table[index] = root;
            afterPut(root);
            return;
        }

        Node<K, V> node = root;
        Node<K, V> parent = node;
        int h1 = newNode.hashCode;
        K k1 = newNode.key;
        int cmp;
        do {
    
    
            parent = node;
            int h2 = node.hashCode;
            K k2 = node.key;
            if (h1 > h2) {
    
    
                cmp = 1;
            } else if (h1 < h2) {
    
    
                cmp = -1;
            } else if (k1 != null && k2 != null && k1.getClass() == k2.getClass() && k1 instanceof Comparable
                    && (cmp = ((Comparable) k1).compareTo(k2)) != 0
            ) {
    
    
            } else {
    
    
                cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
            }

            if (cmp > 0) {
    
    
                node = node.right;
            } else if (cmp < 0) {
    
    
                node = node.left;
            }
        } while (null != node);

        newNode.parent = parent;
        if (cmp > 0) {
    
    
            parent.right = newNode;
        } else {
    
     // cmp < 0
            parent.left = newNode;
        }
        afterPut(newNode);
    }

    // 通过hash函数计算索引
    private int hash(K key) {
    
    
        if (key == null) return 0;
        int hashCode = key.hashCode();
        hashCode = (hashCode ^ (hashCode >>> 16));
        return hashCode & (table.length - 1);
    }

    // 通过hash函数计算索引
    private int hash(Node<K, V> node) {
    
    
        int hashCode = node.hashCode;
        hashCode = (hashCode ^ (hashCode >>> 16));
        return hashCode & (table.length - 1);
    }

    private V remove(Node<K, V> node) {
    
    
        V oldValue = node.value;
        if (node.hasTwoChildren()) {
    
    
            Node<K, V> s = successor(node);
            node.key = s.key;
            node.value = s.value;
            node.hashCode = s.hashCode;
            node = s;
        }
        int index = hash(node);
        Node<K, V> replacement = node.left == null ? node.right : node.left;
        if (replacement == null) {
    
     // 叶子节点
            if (node.parent == null) {
    
    
                table[index] = null;
            } else if (node.parent.left == node) {
    
    
                node.parent.left = null;
            } else {
    
    
                node.parent.right = null;
            }
        } else {
    
     // 度为1的节点
            replacement.parent = node.parent;
            if (node.parent == null) {
    
    
                table[index] = replacement;
            } else if (node.parent.left == node) {
    
    
                node.parent.left = replacement;
            } else {
    
    
                node.parent.right = replacement;
            }
        }

        size--;

        afterRemove(node, replacement);
        return oldValue;
    }

    private void afterRemove(Node<K, V> node, Node<K, V> replacement) {
    
    
        /* 注意:B树中,真正被删除的节点都是叶子节点 */
        if (isRed(node)) return; // 删除的是红色节点,不用做任何处理
        if (isRed(replacement)) {
    
     // 用以替代的子节点是红色节点
            black(replacement);
            return;
        }

        /* 其余情况(删除的是黑色叶子节点) */

        // 删除的是根节点
        if (node.parent == null) return;

        Node<K, V> parent = node.parent;

        // 获取兄弟节点
        // 叶子节点删除时如果是左孩子,会执行node.parent.left = null;
        boolean isLeftChild = parent.left == null || node.isLeftOfParent();
        Node<K, V> sibling = isLeftChild ? parent.right : parent.left;

        if (isLeftChild) {
    
     // 被删除的叶子节点在左边,兄弟节点在右边
            // 与 “被删除的叶子节点在右边,兄弟节点在左边” 是对称的
            // 旋转更新方向、用左边变为用右边、用右边变为用左边
            if (isRed(sibling)) {
    
    
                black(sibling);
                red(parent);
                rotateLeft(parent);
                sibling = parent.right;
            }
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
    
    
                boolean isBlackParent = isBlack(parent);
                red(sibling);
                black(parent);
                if (isBlackParent) {
    
    
                    afterRemove(parent, null);
                }
            } else {
    
    
                if (isBlack(sibling.right)) {
    
    
                    rotateRight(sibling);
                    sibling = parent.right;
                }
                color(sibling, colorOf(parent));
                black(parent);
                black(sibling.right);
                rotateLeft(parent);
            }
        } else {
    
     // 被删除的叶子节点在右边,兄弟节点在左边(老师PPT上画的情况)
            if (isRed(sibling)) {
    
     // 判断兄弟节点是否为RED
                black(sibling);
                red(parent);
                rotateRight(parent);
                // 更新sibling
                sibling = parent.left;
            }

            // 以下情况,兄弟结点肯定为黑色

            // 判断sibling是否至少有1个RED子节点
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
    
     // sibling没有任何RED子节点
                boolean isBlackParent = isBlack(parent);
                red(sibling);
                black(parent);
                if (isBlackParent) {
    
    
                    afterRemove(parent, null);
                }
            } else {
    
     // sibling至少有一个RED子节点 可以向兄弟借元素
                if (isBlack(sibling.left)) {
    
     // 如果兄弟节点的左边是黑色,兄弟先旋转
                    rotateLeft(sibling);
                    sibling = parent.left;
                }

                // 先染色,后旋转
                color(sibling, colorOf(parent));
                black(parent);
                black(sibling.left);
                rotateRight(parent);
                //先旋转,后染色
//                rotateRight(parent);
//                color(sibling, colorOf(parent));
//                black(sibling.left);
//                black(sibling.right);
            }

        }

    }

    // 后继节点
    private Node<K, V> successor(Node<K, V> node) {
    
    
        if (node == null) return null;

        Node<K, V> s = node.right;
        if (s != null) {
    
    
            while (s.left != null) {
    
    
                s = s.left;
            }
            return s;
        }

        while (node.parent != null && node != node.parent.left) {
    
    
            node = node.parent;
        }
        return node.parent;
    }

    private void afterPut(Node<K, V> node) {
    
    
        Node<K, V> parent = node.parent;
        if (parent == null) {
    
     // 添加的是根节点 或者 上溢到了根节点
            black(node);
            return;
        }

        if (isBlack(parent)) {
    
     // parent为BLACK 不用做任何处理 4种情况
            return;
        }

        Node<K, V> uncle = parent.sibling();
        Node<K, V> grand = parent.parent;
        if (isRed(uncle)) {
    
     // 会上溢 4种情况
            black(parent);
            black(uncle);
            red(grand);
            afterPut(grand);
        } else {
    
     // 4种情况
            if (parent.isLeftOfParent()) {
    
     // L
                if (node.isLeftOfParent()) {
    
     // LL
                    black(parent);
                    red(grand);
                    rotateRight(grand);
                } else {
    
     // LR
                    black(node);
                    red(grand);
                    rotateLeft(parent);
                    rotateRight(grand);
                }
            } else {
    
     // R
                if (node.isLeftOfParent()) {
    
     // RL
                    black(node);
                    red(grand);
                    rotateRight(parent);
                    rotateLeft(grand);
                } else {
    
     // RR
                    black(parent);
                    red(grand);
                    rotateLeft(grand);
                }
            }
        }

    }


    // 右旋转
    private void rotateRight(Node<K, V> g) {
    
    
        Node<K, V> p = g.left;
        if (p.right != null) p.right.parent = g;
        g.left = p.right;
        p.right = g;
        afterRotate(g, p);
    }

    // 左旋转
    private void rotateLeft(Node<K, V> g) {
    
    
        Node<K, V> p = (Node<K, V>) g.right;
        if (p.left != null) p.left.parent = g;
        g.right = p.left;
        p.left = g;
        afterRotate(g, p);
    }

    private void afterRotate(Node<K, V> g, Node<K, V> p) {
    
    
        if (g.parent != null) {
    
    
            if (g.isLeftOfParent()) {
    
    
                g.parent.left = p;
            } else {
    
    
                g.parent.right = p;
            }
        } else {
    
    
            table[hash(g)] = p;
        }
        p.parent = g.parent;
        g.parent = p;
    }

    // 给节点上色(染色)
    private Node<K, V> color(Node<K, V> node, boolean color) {
    
    
        if (null == node) return null;
        final Node<K, V> rbNode = node;
        rbNode.color = color;
        return rbNode;
    }

    // 将节点染为黑色
    private Node<K, V> black(Node<K, V> node) {
    
    
        return color(node, Node.BLACK);
    }

    // 将节点染为黑色
    private Node<K, V> red(Node<K, V> node) {
    
    
        return color(node, Node.RED);
    }

    // 判断该节点是什么颜色
    private boolean colorOf(Node<K, V> node) {
    
    
        // 红黑树中的空节点是黑色节点
        return node == null ? Node.BLACK : node.color;
    }

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

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

    private static class Node<K, V> {
    
    

        // 建议新添加的节点默认为RED
        private static final boolean RED = false;
        private static final boolean BLACK = true;

        // 建议新添加的节点默认为RED
        boolean color;
        int hashCode;
        K key;
        V value;
        Node<K, V> left;
        Node<K, V> right;
        Node<K, V> parent;

        Node(K key, V value, Node<K, V> parent) {
    
    
            this.hashCode = (key == null ? 0 : key.hashCode());
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        boolean isLeftOfParent() {
    
    
            return parent != null && parent.left == this;
        }

        boolean isRightOfParent() {
    
    
            return parent != null && parent.right == this;
        }

        boolean hasTwoChildren() {
    
    
            return left != null && right != null;
        }

        boolean isLeaf() {
    
    
            return left == null && right == null;
        }

        // 返回兄弟节点
        Node<K, V> sibling() {
    
    
            if (isLeftOfParent()) return parent.right;
            if (isRightOfParent()) return parent.left;
            return null;
        }

        @Override
        public String toString() {
    
    
            String red = (color == RED ? "R" : "");
            return red + "{" + key + "_" + value + "}";
        }

    }

    public void print() {
    
    
        if (size == 0) return;
        for (int i = 0; i < table.length; i++) {
    
    
            final Node<K, V> root = table[i];
            System.out.println("【index = " + i + "】");
            BinaryTrees.println(new BinaryTreeInfo() {
    
    
                @Override
                public Object string(Object node) {
    
    
                    return node;
                }

                @Override
                public Object root() {
    
    
                    return root;
                }

                @Override
                public Object right(Object node) {
    
    
                    return ((Node<K, V>) node).right;
                }

                @Override
                public Object left(Object node) {
    
    
                    return ((Node<K, V>) node).left;
                }
            });
            System.out.println("---------------------------------------------------");
        }
    }

}

TreeMap vs HashMap

在这里插入图片描述

TreeMap:O(logn)

HashMap:O(1)

LinkedHashMap

实现原理

在这里插入图片描述

删除注意点

在这里插入图片描述

更换节点的连接位置

在这里插入图片描述

在这里插入图片描述

注意点

  • LiknedHashMap中维护的是双向链表(老师图中画的是单向的)

  • 图中老师只画了一颗红黑树串起来的情况,实际上,节点之间是可以跨树(一个索引下就有一颗红黑树)串起来的,因为都会调用createNewNode方法

  • 删除度为2的节点时,交换的是节点在链表中的位置

实现LinkedHashMap

public class LinkedHashMap<K, V> extends HashMap<K, V> {
    
    

    private LinkedNode<K, V> first;
    private LinkedNode<K, V> last;

    @Override
    public void clear() {
    
    
        super.clear();
        first = null;
        last = null;
    }

    @Override
    public boolean containsValue(V value) {
    
    
        LinkedNode<K, V> node = first;
        while (null != node) {
    
    
            if (Objects.equals(value, node.value)) return true;
            node = node.next;
        }
        return false;
    }

    @Override
    public void traversal(Visitor<K, V> visitor) {
    
    
        if (null == visitor) return;

        LinkedNode<K, V> node = first;
        while (null != node) {
    
    
            if (visitor.visit(node.key, node.value)) return;
            node = node.next;
        }
    }

    @Override
    protected Node<K, V> createNewNode(K key, V value, Node<K, V> parent) {
    
    
        LinkedNode<K, V> node = new LinkedNode(key, value, parent);
        if (first == null) {
    
    
            first = node;
            last = node;
        } else {
    
    
            last.next = node;
            node.prev = last;
            last = node;
        }
        return node;
    }

    protected void removeAfter(Node<K, V> willNode, Node<K, V> removedNode) {
    
    
        LinkedNode<K, V> linkedWillNode = (LinkedNode<K, V>) willNode;
        LinkedNode<K, V> linkedRemoveNode = (LinkedNode<K, V>) removedNode;
        if (linkedWillNode != linkedRemoveNode) {
    
    
            // 交换prev
            LinkedNode<K, V> tmp = linkedWillNode.prev;
            linkedWillNode.prev = linkedRemoveNode.prev;
            linkedRemoveNode.prev = tmp;
            if (linkedWillNode.prev == null) {
    
    
                first = linkedWillNode;
            } else {
    
    
                linkedWillNode.prev.next = linkedWillNode;
            }
            if (linkedRemoveNode.prev == null) {
    
    
                first = linkedRemoveNode;
            } else {
    
    
                linkedRemoveNode.prev.next = linkedRemoveNode;
            }

            // 交换next
            tmp = linkedWillNode.next;
            linkedWillNode.next = linkedRemoveNode.next;
            linkedRemoveNode.next = tmp;
            if (linkedWillNode.next == null) {
    
    
                last = linkedWillNode;
            } else {
    
    
                linkedWillNode.next.prev = linkedWillNode;
            }
            if (linkedRemoveNode.next == null) {
    
    
                last = linkedRemoveNode;
            } else {
    
    
                linkedRemoveNode.next.prev = linkedRemoveNode;
            }
        }
        LinkedNode<K, V> prev = linkedRemoveNode.prev;
        LinkedNode<K, V> next = linkedRemoveNode.next;
        if (prev == null) {
    
    
            first = next;
        } else {
    
    
            prev.next = next;
        }
        if (next == null) {
    
    
            last = prev;
        } else {
    
    
            last.prev = prev;
        }
    }

    protected class LinkedNode<K, V> extends Node<K, V> {
    
    
        LinkedNode<K, V> prev;
        LinkedNode<K, V> next;

        LinkedNode(K key, V value, Node<K, V> parent) {
    
    
            super(key, value, parent);
        }
    }

}

参考

小码哥李明杰老师课程: 恋上数据结构与算法 第一季.


本文完,感谢您的关注支持!


猜你喜欢

转载自blog.csdn.net/weixin_44018671/article/details/121480113