美文网首页
二十二、LinkedHashMap & HashSet & Li

二十二、LinkedHashMap & HashSet & Li

作者: 咸鱼Jay | 来源:发表于2022-02-08 16:38 被阅读0次

    1、LinkedHashMap

    • HashMap的基础上维护元素的添加顺序,使得遍历的结果是遵从添加顺序的
      假设添加顺序是37、21、31、41、97、95、52、42、83
    • 删除度为2的节点node
      需要注意更换node与 前驱\后继节点 的连接位置

    1.1、代码实现:

    这里要实现LinkedHashMap,则继承HashMap,因为是需要一个链表将所有红黑树的节点进行链接,而且这个链表还需要是一个双向链表,那么之前的HashMap里的Node类里的属性是不够的,因此需要重新第一个子类的Node

    public class LinkedHashMap<K, V> extends HashMap<K, V> {
        
        private static class LinkedNode<K, V> extends Node<K, V> {
            LinkedNode<K, V> prev;
            LinkedNode<K, V> next;
            public LinkedNode(K key, V value, Node<K, V> parent) {
                super(key, value, parent);
            }
        }
    }
    

    HashMap里面的put方法里Node节点是写死的,这里还需要特殊处理一下:

    public class HashMap<K, V> implements Map<K, V> {
        public V put(K key, V value) {
            ......
            if(root == null) {
                root = createNode(key, value, null);
                table[index] = root;//放入桶中
                size++;
                afterPut(root);// 修复红黑树的性质
                return null;
            }
            ......
    
            // 看看插入到父节点的哪个位置
            Node<K, V> newNode = createNode(key, value, parent);
            ......
            return null;
        }
         
         protected Node<K, V> createNode(K key, V value, Node<K, V> parent) {
            return new Node<>(key, value, parent);
        }
    }
    

    然后在LinkedHashMap类里重写createNode方法

    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
        protected Node<K, V> createNode(K key, V value, Node<K, V> parent) {
            LinkedNode node = new LinkedNode(key, value, parent);
            if(first == null) {// 新添加节点
                first = last = node;
            }else {
                last.next = node;// 新节点添加到已有到后面
                node.prev = last;// 指定新节点前驱节点
                last = node;// 更新最后一个节点
            }
            return node;
        }
        
        private static class LinkedNode<K, V> extends Node<K, V> {
            LinkedNode<K, V> prev;
            LinkedNode<K, V> next;
            public LinkedNode(K key, V value, Node<K, V> parent) {
                super(key, value, parent);
            }
        }
    }
    

    1.1.1、遍历实现

    这里的遍历需要通过链表进行遍历了

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

    1.1.2、删除操作

    如果在删除的时候 ,直接重写HashMapafterRemove方法是不合理的,因为有下面三方面原因:
    1. afterRemove方法是专门用来处理红黑树的。
    2. remove方法里并不是每个地方(例如删除的是根节点)都调用了afterRemove方法。
    3. afterRemove方法传递的参数并不一定都是被删除节点。
    所以这里可以重写remove方法:

    protected V remove(Node<K, V> node) {
        if(node == null) return null;
        LinkedNode<K, V> linkedNode = (LinkedNode<K, V>) node;
        
        LinkedNode<K, V> prev = linkedNode.prev;
        LinkedNode<K, V> next = linkedNode.next;
        if(prev == null) {// 被删除节点是头节点
            first = next;
        }else {
            prev.next = next;
        }
        
        if(next == null) {// 被删除节点是尾节点
            last = prev;
        }else {
            next.prev = prev;
        }
        return super.remove(node);
    }
    

    上面的删除逻辑如果是删除节点是度为2的就会有问题了,因为度为2的删除的逻辑是找的它的后继节点,然后替换掉,最后在删除它的后继节点。

    • 删除度为2的节点node时(比如删除31)
      • 需要注意更换 node 与 前驱\后继节点 的连接位置


        image.png

    那么这样的话重写remove方法就不适合了。因为afterRemove方法是专门用来处理红黑树的,所以这里将名称修改为fixAfterRemove

    public class HashMap<K, V> implements Map<K, V> {
        
        private V remove(Node<K, V> node) {
            if (node == null) return null;
            
            Node<K, V> willNode = node;
            
            ......
            
            if (replacement != null) { // node是度为1的节点
                // 更改parent
                replacement.parent = node.parent;
                // 更改parent的left、right的指向
                if (node.parent == null) { // node是度为1的节点并且是根节点
                    table[index] = replacement;
                } else if (node == node.parent.left) {
                    node.parent.left = replacement;
                } else { // node == node.parent.right
                    node.parent.right = replacement;
                }
                
                // 删除节点之后的处理
                fixAfterRemove(replacement);
            } else if (node.parent == null) { // node是叶子节点并且是根节点
                table[index] = null;
            } else { // node是叶子节点,但不是根节点
                if (node == node.parent.left) {
                    node.parent.left = null;
                } else { // node == node.parent.right
                    node.parent.right = null;
                }
                
                // 删除节点之后的处理
                fixAfterRemove(node);
            }
            
            // 交给子类去处理
            afterRemove(willNode, node);
            return oldValue;
        }
         /**
         * @param willNode 本来想删除的节点
         * @param removedNode 真正删除的节点
         */
        protected void afterRemove(Node<K, V> willNode, Node<K, V> removedNode) { }
    }
    
    public class LinkedHashMap<K, V> extends HashMap<K, V> {
        ......
        @Override
        protected void afterRemove(Node<K, V> willNode ,Node<K, V> removedNode) {
            LinkedNode<K, V> node1 = (LinkedNode<K, V>) willNode;
            LinkedNode<K, V> node2 = (LinkedNode<K, V>) removedNode;
            
            if(node1 != node2) {// 度为2的节点
                //  交换linkedWillNode和linkedRemovedNode在链表中的位置
                // 交换prev
                LinkedNode<K, V> tmp = node1.prev;
                node1.prev = node2.prev;
                node2.prev = tmp;
                if (node1.prev == null) {
                    first = node1;
                } else {
                    node1.prev.next = node1;
                }
                if (node2.prev == null) {
                    first = node2;
                } else {
                    node2.prev.next = node2;
                }
                
                // 交换next
                tmp = node1.next;
                node1.next = node2.next;
                node2.next = tmp;
                if (node1.next == null) {
                    last = node1;
                } else {
                    node1.next.prev = node1;
                }
                if (node2.next == null) {
                    last = node2;
                } else {
                    node2.next.prev = node2;
                }
            }
            
            LinkedNode<K, V> prev = node2.prev;
            LinkedNode<K, V> next = node2.next;
            if(prev == null) {// 被删除节点是头节点
                first = next;
            }else {
                prev.next = next;
            }
            
            if(next == null) {// 被删除节点是尾节点
                last = prev;
            }else {
                next.prev = prev;
            }
        }
     
         ......
    }
    

    1.1.3、containsValue方法

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

    2、HashSet实现

    利用HashMap来实现HashSet

    public class HashSet<E> implements Set<E> {
        private HashMap<E, Object> map = new HashMap<>();
    
        @Override
        public int size() {
            return map.size();
        }
    
        @Override
        public boolean isEmpty() {
            return map.isEmpty();
        }
    
        @Override
        public void clear() {
            map.clear();
        }
    
        @Override
        public boolean contains(E element) {
            return map.containsKey(element);
        }
    
        @Override
        public void add(E element) {
            map.put(element, null);
        }
    
        @Override
        public void remove(E element) {
            map.remove(element);
        }
    
        @Override
        public void traversal(Visitor<E> visitor) {
            map.traversal(new Map.Visitor<E, Object>() {
                public boolean visit(E key, Object value) {
                    return visitor.visit(key);
                }
            });
        }
    }
    

    3、LinkedHashSet实现

    利用LinkedHashMap来实现LinkedHashSet

    public class LinkedHashSet<E> implements Set<E> {
        private LinkedHashMap<E, Object> map = new LinkedHashMap<>();
    
        @Override
        public int size() {
            return map.size();
        }
    
        @Override
        public boolean isEmpty() {
            return map.isEmpty();
        }
    
        @Override
        public void clear() {
            map.clear();
        }
    
        @Override
        public boolean contains(E element) {
            return map.containsKey(element);
        }
    
        @Override
        public void add(E element) {
            map.put(element, null);
        }
    
        @Override
        public void remove(E element) {
            map.remove(element);
        }
    
        @Override
        public void traversal(Visitor<E> visitor) {
            map.traversal(new Map.Visitor<E, Object>() {
                public boolean visit(E key, Object value) {
                    return visitor.visit(key);
                }
            });
        }
    }
    

    相关文章

      网友评论

          本文标题:二十二、LinkedHashMap & HashSet & Li

          本文链接:https://www.haomeiwen.com/subject/zunrkrtx.html