Day31-数据结构与算法-集合


title: Day31-数据结构与算法-集合
date: 2020-11-09 16:45:17
author: 子陌


常用的经典数据结构

集合(Set)

  • 集合的特点

    • 不存放重复的元素:常用于去重(统计新增IP、统计词汇量等)
  • 集合的实现方式:

    • 动态数组
    • 链表
    • 二叉搜索树(AVL树、红黑树)

Set集合实现

  • Set公共接口
package com.zimo.集合;

/**
 * 集合 - Set公共接口
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/9 16:55:52
 */
public interface Set<E> {
    
    
    int size();
    boolean isEmpty();
    void clear();
    boolean contains(E element);
    void add(E element) ;
    void remove(E element) ;
    void traversal(Visitor<E> visitor);

    public static abstract class Visitor<E> {
    
    
        boolean stop;
        public abstract boolean visit(E element) ;
    }
}
  • LinkedList实现Set集合
package com.zimo.集合;

import com.zimo.线性表.List;
import com.zimo.线性表.链表.DupLinkList;

/**
 * 集合Set实现 - LinkedList方式实现
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/9 17:28
 */
public class ListSet<E> implements Set<E> {
    
    
    private List<E> list = new DupLinkList<>();     // DupLinkList自己实现的LinkedLsit
    @Override
    public int size() {
    
    
        return list.size();
    }

    @Override
    public boolean isEmpty() {
    
    
        return list.isEmpty();
    }

    @Override
    public void clear() {
    
    
        list.clear();
    }

    @Override
    public boolean contains(E element) {
    
    
        return list.contains(element);
    }

    @Override
    public void add(E element) {
    
    
        if(element == null) throw new NullPointerException("添加元素不能为空!");
        if (contains(element)) return;
        list.add(element);
        // if (list.indexOf(element) != List.ELEMENT_NOT_FOUND)
        //      list.set(list.indexOf(element),element);
        // esle
        //      list.add(element);
    }

    @Override
    public void remove(E element) {
    
    
        int index = list.indexOf(element);
        if (index != List.ELEMENT_NOT_FOUND){
    
    
            list.remove(index);
        }
    }

    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        if (visitor == null) return;
        int size = list.size();
        for (int i = 0; i < size; i++) {
    
    
            if (visitor.visit(list.get(i))) {
    
    
                return;
            }
        }
    }

    // 测试set集合
    public static void main(String[] args) {
    
    
        ListSet<Integer> set = new ListSet<>();
        for (int i = 0; i < 5; i++) {
    
    
            set.add(i + 10);
        }
        set.add(11);
        set.add(12);
        set.traversal(new Visitor<Integer>() {
    
    
            @Override
            public boolean visit(Integer element) {
    
    
                System.out.println(element);
                return false;
            }
        });
    }
}
  • 红黑树实现Set
package com.zimo.集合;

import com.zimo..BinaryTree;
import com.zimo..二叉搜索树.RedBlackTree;
import java.util.Comparator;
/**
 *  集合Set实现 - RedBlackTree方式实现(限制:元素必须具备可比较性)
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/9 17:52:14
 */
public class TreeSet<E> implements Set<E>{
    
    
    private RedBlackTree<E> tree = new RedBlackTree<>();    // 元素必须具备可比较性

    public TreeSet() {
    
    
        this(null);
    }
    public TreeSet(Comparator<E> comparator) {
    
    
        tree = new RedBlackTree<>(comparator);
    }
    @Override
    public int size() {
    
    
        return tree.size();
    }
    @Override
    public boolean isEmpty() {
    
    
        return tree.isEmpty();
    }
    @Override
    public void clear() {
    
    
        tree.clear();
    }
    @Override
    public boolean contains(E element) {
    
    
        return tree.contains(element);
    }
    @Override
    public void add(E element) {
    
    
        tree.add(element);
    }
    @Override
    public void remove(E element) {
    
    
        tree.remove(element);
    }
    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        // 中序遍历更有意义,从小到大
        tree.inorderTraversal(new BinaryTree.Visitor<E>() {
    
    
            @Override
            public boolean visitor(E element) {
    
    
                return visitor.visit(element);
            }
        });
    }
    public static void main(String[] args) {
    
    
        TreeSet<Integer> treeSet = new TreeSet<>();
        for (int i = 0; i < 5; i++) {
    
    
            treeSet.add(10 + i);
        }
        treeSet.add(11);
        treeSet.add(12);
        treeSet.add(11);
        treeSet.traversal(new Visitor<Integer>() {
    
    
            @Override
            public boolean visit(Integer element) {
    
    
                System.out.println(element);
                return false;
            }
        });
    }
}

映射(Map)

  • Map在有些编程语言中也叫做字典(dictionary,比如Python、Objective-C、Swift等)
  • Map的每个key都是唯一的
  • 类似Set,Map可以利用链表、二叉搜索树(AVL、红黑树)等数据结构来实现
  • Set可以利用Map来实现(当Map只保留Key时,键唯一,就是一个Set集合)

红黑树实现TreeMap

package com.zimo.映射;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.Queue;

/**
 * Map - 红黑树实现(将map当作红黑树实现,而不是套用红黑树)
 *      可以使用包装实现class {K k,V v} 但不推荐使用
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/10 10:43
 */
public class TreeMap<K,V> implements Map<K,V>{
    
    
    private static final boolean RED = false;
    private static final boolean BLACK = true;

    private int size;
    private Node<K,V> root;

    private Comparator<K> comparator;

    public TreeMap() {
    
    
        this(null);
    }

    public TreeMap(Comparator<K> comparator) {
    
    
        this.comparator = comparator;
    }

    public int size() {
    
    
        return size;
    }

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

    public void clear() {
    
    
        root = null;
        size = 0;
    }

    @Override
    public V put(K key, V value) {
    
    
        keyNotNullCheck(key);
        if (root == null) {
    
    
            root = new Node<>(key, value, null);
            size++;
            afterPut(root);
            return null;
        } else {
    
    
            Node<K,V> node = this.root;
            Node<K,V> parent = null;
            int compare = 0;
            while (node != null) {
    
    
                compare = compare(key, node.key);
                parent = node;
                if (compare > 0) {
    
    
                    node = node.right;
                } else if (compare < 0) {
    
    
                    node = node.left;
                } else {
    
     // 相等
                    node.key = key;
                    V oldValue = node.value;
                    node.value = value;
                    return oldValue;
                }
            }
            Node<K,V> newNode = new Node<>(key,value, parent);
            if (compare > 0) {
    
    
                parent.right = newNode;
            } else {
    
    
                parent.left = newNode;
            }
            size++;
            afterPut(newNode);
            return null;
        }
    }

    @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;
    }

    @Override
    public boolean containsValue(V value) {
    
    
        // value不具备可比较性,使用层序遍历实现
        if (root == null) return false;
        Queue<Node<K,V>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
    
    
            Node<K,V> node = queue.poll();
            if (valEquals(value, node.value)){
    
    
                return true;
            }
            if (node.left != null){
    
    
                queue.offer(node.left);
            }
            if (node.right != null){
    
    
                queue.offer(node.right);
            }
        }
        return false;
    }

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

    private void traversal(Node<K,V> node, Visitor<K,V> visitor) {
    
    
        if (node == null || visitor.stop) return;
        traversal(node.left, visitor);
        if (visitor.stop) return;
        visitor.visit(node.key,node.value);
        traversal(node.right,visitor);
    }

    private V remove(Node<K, V> node) {
    
    
        if (node == null) return null;
        V oldValue = node.value;
        size--;
        if (node.hasTwoChildren()) {
    
    
            Node<K,V> successor = successor(node);
            node.key = successor.key;
            node.value = successor.value;
            node = successor;
        }
        Node<K,V> replacement = node.left != null ? node.left : node.right;
        if (replacement != null) {
    
    
            replacement.parent = node.parent;   // 更改parent
            if (node.parent == null) {
    
       // 度为1的根节点
                root = replacement;
            } else if (node == node.parent.left) {
    
    
                node.parent.left = replacement;
            } else {
    
    
                node.parent.right = replacement;
            }
            afterRemove(node, replacement);
        } else if (node.parent == null) {
    
    
            root = null;
            afterRemove(node, null);
        } else {
    
    
            if (node == node.parent.left) {
    
    
                node.parent.left = null;
            } else {
    
    
                node.parent.right = null;
            }
            afterRemove(node, null);
        }
        return oldValue;
    }

    private Node<K,V> node(K key) {
    
    
        Node<K,V> node = this.root;
        while (node != null) {
    
    
            int cmp = compare(key, node.key);
            if (cmp == 0) return node;
            if (cmp > 0) node = node.right;
            else node = node.left;
        }
        return null;
    }

    private void afterPut(Node<K,V> node) {
    
    
        Node<K,V> parent = node.parent;
        if (parent == null){
    
    
            black(node);
            return;
        }
        if (isBlack(parent)){
    
     return; }
        Node<K,V> grand = red(parent.parent);
        Node<K,V> uncle = parent.sibling();
        if (isRed(uncle)){
    
      // 叔父节点是红色[B树节点上溢]
            black(parent);
            black(uncle);
            afterPut(grand);
            return;
        }
        if (parent.isLeftChild()){
    
      // L
            if (node.isLeftChild()){
    
        // LL
                black(parent);
            }else {
    
     // LR
                black(node);
                rotateLeft(parent);
            }
            rotateRight(grand);
        }else {
    
         // R
            if (node.isLeftChild()){
    
        // RL
                black(node);
                rotateRight(parent);
            }else {
    
     // RR
                black(parent);
            }
            rotateLeft(grand);
        }
    }
    private void afterRemove(Node<K,V> node, Node<K,V> replacement) {
    
    
        if (isRed(node)) return;
        if (isRed(replacement)){
    
    
            black(replacement);     // 将替代的子节点直接染成黑色
            return;
        }
        Node<K,V> parent = node.parent;
        if (parent == null) return;
        boolean isleft = parent.left == null || node.isLeftChild(); //  node.isLeftChild()为了包含下面父节点删除情况
        Node<K,V> sibling = isleft ? parent.right : parent.left;
        if (isleft){
    
        // 被删除的节点在左边,兄弟在右边
            if (isRed(sibling)) {
    
    
                black(sibling);
                red(parent);
                rotateLeft(parent);
                sibling = parent.right;
            }
            if (isBlack(sibling.left) && isBlack(sibling.right)){
    
    
                boolean pIsBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (pIsBlack) {
    
    
                    afterRemove(parent,null);
                }
            }else{
    
    
                if (isBlack(sibling.right)){
    
       // 兄弟节点的左边是黑色,兄弟要先左旋转
                    rotateRight(sibling);
                    sibling = parent.right;
                }
                color(sibling, colorOf(parent));
                black(sibling.right);
                black(parent);
                rotateLeft(parent);
            }
        }else {
    
         // 被删除的节点在右边,兄弟在左边
            if (isRed(sibling)) {
    
    
                black(sibling);
                red(parent);
                rotateRight(parent);
                sibling = parent.left;
            }
            if (isBlack(sibling.left) && isBlack(sibling.right)){
    
    
                boolean pIsBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (pIsBlack) {
    
    
                    afterRemove(parent,null);
                }
            }else{
    
    
                if (isBlack(sibling.left)){
    
       // 兄弟节点的左边是黑色,兄弟要先左旋转
                    rotateLeft(sibling);
                    sibling = parent.left;
                }
                color(sibling, colorOf(parent));
                black(sibling.left);
                black(parent);
                rotateRight(parent);
            }
        }
    }
    // 左旋
    private void rotateLeft(Node<K,V> node){
    
    
        Node<K,V> parent = node.right;
        Node<K,V> child = parent.left;
        node.right = child;
        parent.left = node;
        afterRotate(node, parent, child);
    }
    // 右旋
    private void rotateRight(Node<K,V> node){
    
    
        Node<K,V> parent = node.left;
        Node<K,V> child = parent.right;
        node.left = child;
        parent.right = node;
        afterRotate(node, parent, child);
    }
    private void afterRotate(Node<K,V> node, Node<K,V> parent, Node<K,V> child) {
    
    
        parent.parent = node.parent;
        if (node.isLeftChild()) {
    
    
            node.parent.left = parent;
        } else if (node.isRightChild()) {
    
    
            node.parent.right = parent;
        } else {
    
    
            root = parent;
        }
        if (child != null) child.parent = node;
        node.parent = parent;
    }

    private Node<K,V> predesessor(Node<K,V> node) {
    
    
        if (node == null) return null;
        Node<K,V> p = node.left;
        if (p != null) {
    
    
            while (p.right != null) p = p.right;
            return p;
        }
        while (node.parent != null && node == node.parent.left) {
    
    
            node = node.parent;
        }
        return node.parent;
    }

    private Node<K,V> successor(Node<K,V> node) {
    
    
        if (node == null) return null;
        Node<K,V> p = node.right;
        if (p != null) {
    
    
            while (p.left != null) p = p.left;
            return p;
        }
        while (node.parent != null && node == node.parent.right) {
    
    
            node = node.parent;
        }
        return node.parent;
    }

    private int compare(K k1, K k2) {
    
    
        if (comparator != null) {
    
    
            return comparator.compare(k1, k2);
        }
        return ((Comparable<K>) k1).compareTo(k2);   // 如果没实现比较器,默认为强制实现比较接口
    }

    private boolean valEquals(V v1, V v2){
    
    
        return v1 == null ? v2 == null : v1.equals(v2);
    }

    private Node<K,V> color(Node<K,V> node, boolean color){
    
    
        if (node == null) return node;
        node.color = color;
        return node;
    }

    private Node<K,V> red(Node<K,V> node){
    
     return color(node, RED); }
    private Node<K,V> black(Node<K,V> node){
    
     return color(node, BLACK); }
    private boolean colorOf(Node<K,V> node){
    
     return node == null ? BLACK :  node.color; }
    private boolean isRed(Node<K,V> node){
    
     return colorOf(node) == RED; }
    private boolean isBlack(Node<K,V> node){
    
     return colorOf(node) == BLACK; }

    private static class Node<K,V>{
    
    
        K key;
        V value;
        boolean color = RED;
        Node<K,V> left;
        Node<K,V> right;
        Node<K,V> parent;

        public Node(K key, V value, Node<K,V> parent) {
    
    
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        public boolean isLeaf() {
    
     return left == null && right == null; }
        public boolean hasTwoChildren() {
    
     return left != null && right != null; }
        public boolean isLeftChild() {
    
     return parent != null && parent.left == this; }
        public boolean isRightChild() {
    
     return parent != null && parent.right == this; }
        public Node<K,V> sibling(){
    
    
            if (isLeftChild()) return parent.right;
            if (isRightChild()) return parent.left;
            return null;
        }
    }

    private void keyNotNullCheck(K Key) {
    
    
        if (Key == null)
            throw new IllegalArgumentException("Key元素不能为空");
    }

    public static void main(String[] args) {
    
    
        TreeMap<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("b", 2);
        treeMap.put("a", 1);
        treeMap.put("a", 3);
        treeMap.put("c", 44);
        treeMap.traversal(new Visitor<String, Integer>() {
    
    
            @Override
            public boolean visit(String key, Integer value) {
    
    
                System.out.println("key:" + key + " --- value:" + value);
                return false;
            }
        });
    }
}

TreeMap优化

// 添加、删除第一个节点可以优化
@Override
public V put(K key, V value) {
    
    
    keyNotNullCheck(key);
    if (root == null) {
    
    
        root = new Node<>(key, value, null);
+       black(root);	// 如果添加根节点,直接染黑,无需修复
        size++;
-       // afterPut(root);
        return null;
    } else {
    
     ... }    
}
 private V remove(Node<K, V> node) {
    
    
     ...
     if (replacement != null) {
    
    
         ...
     } else if (node.parent == null) {
    
    
         root = null;
-        // afterRemove(node, null);	删除根节点,没必要调用afterRemove修复特性
     } else {
    
    
         ...
     }
     return oldValue;
 }

利用Map实现Set集合

package com.zimo.集合;

/**
 * 利用红黑树Map实现Set集合
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/10 14:43
 */
public interface MSet<E> {
    
    
    int size();
    boolean isEmpty();
    void clear();
    boolean contains(E element);
    void add(E element) ;
    void remove(E element) ;
    void traversal(Set.Visitor<E> visitor);

    public static abstract class Visitor<E> {
    
    
        boolean stop;
        public abstract boolean visit(E element) ;
    }
}
package com.zimo.集合;

import com.zimo.映射.Map;
import com.zimo.映射.TreeMap;

/**
 * 利用TreeMap实现Set集合
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/10 14:50
 */
public class TMSet<E> implements MSet<E> {
    
    
    Map<E, Object> map = new TreeMap<>();
    @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(Set.Visitor<E> visitor) {
    
    
        map.traversal(new Map.Visitor<E, Object>() {
    
    
            @Override
            public boolean visit(E key, Object value) {
    
    
                visitor.visit(key);
                return false;
            }
        });
    }
}

TreeMap分析

  • 时间复杂度(平均)
    • 添加、删除、搜索:O(logn)
  • 特点:
    • Key必须具备可比较性
    • 元素的分布是有顺序的
  • 在实际应用中,很多时候的需求
    • Map中存储的元素不需要讲究顺序
    • Map中的Key不需要具备可比较性
  • 不考虑顺序、不考虑Key的可比较性,Map有更好的实现方案,平均实际复杂度可以达到O(1)
    • 那就是采取哈希表来实现Map

哈希表(Hash Table)

  • 哈希表也叫做散列表(hash有“剁碎”的意思)
    哈希表

哈希冲突

  • 哈希冲突也叫做哈希碰撞
    • 2个不同的key,经过哈希函数计算出相同的结果
    • Key1 ≠ Key2,hash(Key1) == hash(Key2)
  • 解决哈希冲突的常见方法
    • 开放定址法(Open Addressing)
      • 按照一定规则向其他地址探测,直到遇到空桶(线性探测、平方探测)
    • 再哈希法(Re-Hashing)
      • 设计多个哈希函数
    • 链地址法(Separate Chaining)
      • 通过链表将同一index的元素串起来

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

Java 官方使用链地址法,默认使用单向链表将元素串起来,在添加元素时,可能会由单向链表转为红黑树来存储元素

  • 比如当哈希表容量 ≥ 64 且单向链表的节点数量大于8时
  • 红黑树节点数量少到一定程度时,又会转为单向链表

JDK1.8中的哈希表是使用链表 + 红黑树解决哈希冲突

  • 为何要使用单向链表
    • 每次都要从头节点开始遍历
    • 单向链表比双向链表少一个指针,可以节省内存空间

哈希函数

  • 哈希表中哈希函数的大致实现步骤

    1. 先生成key的哈希值(必须是整数

    2. 再让key的哈希值数组大小进行相关运算,生成一个索引值

      hash_code(key) % table.length

  • 为了提高效率,可以使用&位运算取代%运算【前提:将数组的长度设计为2的幂(2n)】

    hash_code(key) & table.length - 1

哈希函数位运算

  • 良好的哈希函数

    • 让哈希值更加均匀分布 → 减少哈希冲突次数 → 提升哈希表的性能
如何生成key的哈希值

Java中hash值只能是int大小(4个字节,32位)

  • key的常见种类可能有

    • 整数、浮点数、字符串、自定义对象
    • 不同种类的key,哈希值的生成方式不一样,但是目标是一致的
      • 尽量让每个key的哈希值是唯一的
      • 尽量让key的所有信息参与运算
  • 在Java中,HashMap的key必须实现hashCode、equals方法,也允许key为null

  • 整数

    • 整数值当作哈希值(value = 10 → hashcode = 10)

      return value

  • 浮点数(32位)

    • 使用浮点数在内存中的二进制存储数据,转成整数值当作哈希值

      (value = 10.6 → 100......11010 → hashcode = (int)100......11010 = 1093245338)

      return floatToIntBits(value)

  • Long和Double的哈希值(64位)

    • >>>(无符号右移)和 ^(异或运算) 的作用,(如果用&,高位全是1,相当于没算,|如果高位为1全是1,哈希值冲突太高)

      • 高 32bit 和低 32bit 混合计算出 32bit的哈希值
      • 充分利用所有信息计算出哈希值
    • Long类型(8个字节64位),尽量让64位都参与运算(value)

      return (int)(value ^ (value >>> 32));

    • Double类型

      long bits = doubleToLongBits(value); return (int)(bits ^ (bits >>> 32)); `

    value 1111 1111 1111 1111 1111 1111 1111 1111 1011 0110 0011 1001 0110 1111 1100 1010
    value>>>32 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111
    value ^ (value >>> 32) 1111 1111 1111 1111 1111 1111 1111 1111 0100 1001 1100 0110 1001 0000 0011 0101
  • 字符串

    • 整数1234计算公式:1 * 103 + 2 * 102 + 3 * 101 + 4 * 100
    • 因此字符串jack公式:j * n3 + a * n2 + c * n1 + k * n0,等价于[(j * n + a) * n + c] * n + k
    • 在JDK中,乘数n为31,为什么使用31呢
      • 31是一个奇素数,JVM会将31 * i优化成(i << 5) - i
        • 31不仅仅是符合2^n-1,它是个奇素数(既是奇数,又是素数,也就是质数)
        • 素数和其他数相乘的结果比其他方式更容易产成唯一性,减少哈希冲突
  • 自定义对象

    • 默认hashCode和内存地址有关,如果new了两个数据一样的对象,也会当作是两个不同的对象
    • 重写Object父类的hashCode方法自定义规则,充分利用每个成员变量,可借鉴字符串算法
    • 自定义对象作为key,最好同时重写hashCode、equals方法
      • equals:用以判断2个key是否为同一个key
      • hashCode:必须保证equals为true的2个key的哈希值一样
      • 反过来hashCode相等的key,不一定equals为true
package com.zimo.哈希表;

import static java.lang.Double.doubleToLongBits;
import static java.lang.Float.floatToIntBits;

/**
 * 哈希表 - 哈希值计算
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/11 10:45
 */
public class HashCode {
    
    
    private int code;
    private String name;

    public HashCode(int code, String name) {
    
    
        this.code = code;
        this.name = name;
    }

    @Override
    public int hashCode() {
    
     // 如果code和name值一样,那就认为是同一个对象
        int hashCode = Integer.hashCode(code);
        hashCode = hashCode * 31 + (name != null ? name.hashCode() : 0);
        return hashCode;
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj) return true;
        // obj.getClass() != getClass() 不允许当前类的子类对象,!(obj instanceof HashCode) 允许子类
        if (obj == null || obj.getClass() != getClass()) return false;
        HashCode hashCode = (HashCode)obj;
        return hashCode.code == this.code && hashCode.name == null ? this.name == null : hashCode.name.equals(this.name);
    }

    public static void main(String[] args) {
    
    
        System.out.println(hashCode_Int(110));
        System.out.println(hashCode_Float(10.6f));
        System.out.println(hashCode_Long(156L));
        System.out.println(hashCode_Double(10.9));
        System.out.println(hashCode_String("rose"));
        System.out.println(new HashCode(1, "aa").hashCode());
        System.out.println(new HashCode(1, "aa").hashCode());
    }

    public static int hashCode_Int(int value){
    
    
        return value;
    }

    public static int hashCode_Float(float value){
    
    
        return floatToIntBits(value);
    }

    public static int hashCode_Long(long value){
    
    
        return (int) (value ^ (value >>> 32));
    }

    public static int hashCode_Double(double value){
    
    
        long va = doubleToLongBits(value);
        return hashCode_Long(va);
    }

    public static int hashCode_String(String value){
    
    
        int hashCode = 0;
        for (int i = 0; i < value.length(); i++) {
    
    
            char c = value.charAt(i);
            hashCode = hashCode * 31 + c;   // 等价于:(hashCode << 5) - hashCode + c
        }
        return hashCode;
    }
}

HashMap实现

关于使用%来计算索引

  • 如果使用%计算索引

    • 建议把哈希表的长度设计为素数(质数)

    • 可以大大减小哈希冲突

      10 % 8 = 2 10 % 7 = 3
      20 % 8 = 4 20 % 7 = 6
      30 % 8 = 6 30 % 7 = 2
      40 % 8 = 0 40 % 7 = 5
      50 % 8 = 2 50 % 7 = 1
      60 % 8 = 4 60 % 7 = 4
      70 % 8 = 6 70 % 7 = 0
  • 下面表格列出不同数据规模对应的最佳素数,特点如下

    • 每个素数略小于前一个素数的2倍
    • 每个素数尽可能接近2的幂(2n
下界 上届 素数
25 26 53
26 27 97
27 28 193
28 29 389
29 210 769
210 211 1543
227 228 201326611
228 229 402653189
229 230 805306457
230 231 1610612741
package com.zimo.哈希表;

import com.zimo.映射.Map;

import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;

/**
 * 使用哈希表实现HashMap
 *      直接使用红黑树解决哈希冲突
 *          数组中直接挂红黑树的根节点,不挂红黑树对象,可以省略红黑树的成员(size,comparator...)
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/11 16:12:20
 * @version v0.2 by 2020/11/17 16:06:18	优化索引
 */
public class MyHashMap<K,V> implements Map<K,V> {
    
    
    private static final boolean RED = false;
    private static final boolean BLACK = true;
    private static final int DEFAULT_CAPACITY = 1 << 4; // 设计为2的幂次方,默认为16
    private int size;
    private Node<K,V>[] table;

    public MyHashMap() {
    
    
        this.table = new Node[DEFAULT_CAPACITY];
    }

    @Override
    public int size() {
    
    
        return size;
    }

    @Override
    public boolean isEmpty() {
    
    
        return false;
    }

    @Override
    public void clear() {
    
    
        if (size == 0) return;
        this.size = 0;
        for (int i = 0; i < table.length; i++) {
    
    
            table[i] = null;
        }
    }

    @Override
    public V put(K key, V value) {
    
    
        // 1.拿到索引
        int index = index(key);
        // 2.取出index位置的红黑树根节点
        Node<K, V> root = table[index];
        if (root == null) {
    
         // 新节点
            root = new Node<>(key, value, null);
            table[index] = root;
            size++;
            afterPut(root);
            return null;
        }
        // 哈希冲突
        Node<K, V> parent = root;
        Node<K, V> node = root;
        int cmp = 0;
        K key1 = key;
        int hashCode1 = hash(key1);
        Node<K, V> result = null;
        boolean searched = false;   // 是否已经搜索完这个key了
        do {
    
    
            parent = node;
            K key2 = node.key;
            int hashCode2 = node.hashCode;
            if (hashCode1 > hashCode2){
    
    
                cmp = 1;
            }else if (hashCode1 < hashCode2){
    
    
                cmp = -1;
            }else if (Objects.equals(key1,key2)){
    
    
                cmp = 0;
            }else if (key1 != null && key2 != null
                    && key1.getClass() == key2.getClass()
                    && key1 instanceof Comparable
                    && (cmp = ((Comparable) key1).compareTo(key2)) != 0){
    
       // 防止equals不相等,comparable = 0的情况,被覆盖了
            }else if (searched){
    
        // 扫描过了
                cmp = System.identityHashCode(key1) - System.identityHashCode(key2);
            }else {
    
     // searched == false
                // 先扫描,再根据内存地址比较决定
                if ((node.left != null && (result = node(node.left, key1)) != null)
                        || (node.right != null && (result = node(node.right, key1)) != null)){
    
    
                    // key存在
                    node = result;
                    cmp = 0;
                }else{
    
    
                    // 不存在,如果进到这里,下一次就不用再进行扫描,直接进行地址比较添加就行
                    searched = true;
                    cmp = System.identityHashCode(key1) - System.identityHashCode(key2);
                }
            }
            if (cmp > 0){
    
    
                node = node.right;
            }else if (cmp < 0){
    
    
                node = node.left;
            }else {
    
    
                V oldValue = node.value;
                node.value = value;
                node.hashCode = hashCode1;  // 两个key equals,他们的hashCode相等,所以可以不覆盖
                return oldValue;
            }
        }while (node != null);
        Node<K, V> newNode = new Node<>(key, value, parent);
        if (cmp > 0){
    
    
            parent.right = newNode;
        }else {
    
    
            parent.left = newNode;
        }
        size++;
        afterPut(newNode);
        return null;
    }

    @Override
    public V get(K key) {
    
    
        Node<K,V> node = node(key);
        return node == null ? null : node.value;
    }

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

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

    @Override
    public boolean containsValue(V value) {
    
    
        if (size == 0) return false;
        Queue<Node<K,V>> queue = new LinkedList<>();
        for (int i = 0; i < table.length; i++) {
    
    
            if (table[i] == null) continue;
            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.right != 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) continue;
            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);
                }
            }
        }
    }

    /**
     * 根据key生成对应的索引(在桶数组中的位置)
     * @param key
     * @return
     */
    private int index(K key){
    
    
        return hash(key) & (table.length - 1);
    }
    private int hash(K key){
    
    
        if (key == null) return 0;  // 支持key为空,默认0号位置
        int hashCode = key.hashCode();  // 为了防止别人hashCode不均匀,这边在进行一次高低16位混合运算
        return hashCode ^ (hashCode >>> 16);
    }
    private int index(Node<K,V> node){
    
    
//      return (node.hashCode ^ (node.hashCode >>> 16)) & (table.length - 1);
        return node.hashCode & (table.length - 1);
    }
    // 节点
    private static class Node<K,V>{
    
    
        K key;
        V value;
        int hashCode;
        boolean color = RED;
        Node<K,V> left;
        Node<K,V> right;
        Node<K,V> parent;

        public Node(K key, V value, Node<K,V> parent) {
    
    
            this.key = key;
            this.value = value;
            this.parent = parent;
            int hash = key == null ? 0 : key.hashCode();
            this.hashCode = hash ^ (hash >>> 16);   // 扰动计算
        }

        public boolean hasTwoChildren() {
    
     return left != null && right != null; }
        public boolean isLeftChild() {
    
     return parent != null && parent.left == this; }
        public boolean isRightChild() {
    
     return parent != null && parent.right == this; }
        public Node<K,V> sibling(){
    
    
            if (isLeftChild()) return parent.right;
            if (isRightChild()) return parent.left;
            return null;
        }
    }

    private void afterPut(Node<K,V> node) {
    
    
        Node<K,V> parent = node.parent;
        if (parent == null){
    
    
            black(node);
            return;
        }
        if (isBlack(parent)){
    
     return; }
        Node<K,V> grand = red(parent.parent);
        Node<K,V> uncle = parent.sibling();
        if (isRed(uncle)){
    
      // 叔父节点是红色[B树节点上溢]
            black(parent);
            black(uncle);
            afterPut(grand);
            return;
        }
        if (parent.isLeftChild()){
    
      // L
            if (node.isLeftChild()){
    
        // LL
                black(parent);
            }else {
    
     // LR
                black(node);
                rotateLeft(parent);
            }
            rotateRight(grand);
        }else {
    
         // R
            if (node.isLeftChild()){
    
        // RL
                black(node);
                rotateRight(parent);
            }else {
    
     // RR
                black(parent);
            }
            rotateLeft(grand);
        }
    }
    
    private void afterRemove(Node<K,V> node, Node<K,V> replacement) {
    
    
        if (isRed(node)) return;
        if (isRed(replacement)){
    
    
            black(replacement);     // 将替代的子节点直接染成黑色
            return;
        }
        Node<K,V> parent = node.parent;
        if (parent == null) return;
        boolean isleft = parent.left == null || node.isLeftChild(); //  node.isLeftChild()为了包含下面父节点删除情况
        Node<K,V> sibling = isleft ? parent.right : parent.left;
        if (isleft){
    
        // 被删除的节点在左边,兄弟在右边
            if (isRed(sibling)) {
    
    
                black(sibling);
                red(parent);
                rotateLeft(parent);
                sibling = parent.right;
            }
            if (isBlack(sibling.left) && isBlack(sibling.right)){
    
    
                boolean pIsBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (pIsBlack) {
    
    
                    afterRemove(parent,null);
                }
            }else{
    
    
                if (isBlack(sibling.right)){
    
       // 兄弟节点的左边是黑色,兄弟要先左旋转
                    rotateRight(sibling);
                    sibling = parent.right;
                }
                color(sibling, colorOf(parent));
                black(sibling.right);
                black(parent);
                rotateLeft(parent);
            }
        }else {
    
         // 被删除的节点在右边,兄弟在左边
            if (isRed(sibling)) {
    
    
                black(sibling);
                red(parent);
                rotateRight(parent);
                sibling = parent.left;
            }
            if (isBlack(sibling.left) && isBlack(sibling.right)){
    
    
                boolean pIsBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (pIsBlack) {
    
    
                    afterRemove(parent,null);
                }
            }else{
    
    
                if (isBlack(sibling.left)){
    
       // 兄弟节点的左边是黑色,兄弟要先左旋转
                    rotateLeft(sibling);
                    sibling = parent.left;
                }
                color(sibling, colorOf(parent));
                black(sibling.left);
                black(parent);
                rotateRight(parent);
            }
        }
    }
    // 左旋
    private void rotateLeft(Node<K,V> node){
    
    
        Node<K,V> parent = node.right;
        Node<K,V> child = parent.left;
        node.right = child;
        parent.left = node;
        afterRotate(node, parent, child);
    }
    // 右旋
    private void rotateRight(Node<K,V> node){
    
    
        Node<K,V> parent = node.left;
        Node<K,V> child = parent.right;
        node.left = child;
        parent.right = node;
        afterRotate(node, parent, child);
    }
    private void afterRotate(Node<K,V> node, Node<K,V> parent, Node<K,V> child) {
    
    
        parent.parent = node.parent;
        if (node.isLeftChild()) {
    
    
            node.parent.left = parent;
        } else if (node.isRightChild()) {
    
    
            node.parent.right = parent;
        } else {
    
    
            table[index(node)] = parent;    // 由于同一颗红黑树,随便传一个,计算出桶索引即可
        }
        if (child != null) child.parent = node;
        node.parent = parent;
    }

    private int compare(K k1, K k2, int hashCode1, int hashCode2) {
    
    
        // 1.比较哈希值大小 直接传进来,省的每次都计算
        int result = hashCode1 - hashCode2;
        if (result != 0) return result;         // hashcode不相等,直接返回哈希值结果
        // 2.比较equals
        if (Objects.equals(k1, k2)) return 0;   // hashcode相等,equals相等
        // 3.比较类名
        if(k1 != null && k2 != null){
    
    
            String name1 = k1.getClass().getName();
            String name2 = k2.getClass().getName();
            result = name1.compareTo(name2);
            if (result != 0) return result;     // 类型不同,按类名排序
            if (k1 instanceof Comparable){
    
          // 本身是否具备可比较性
                return ((Comparable) k1).compareTo(k2);
            }
        }
        // 同一种类型,哈希相等,但不具备可比较性
        // k1不为null, k2为null 或者 k1为null, k2不为null
        // 4.使用内存地址比较:System.identityHashCode();
        return System.identityHashCode(k1) - System.identityHashCode(k2);
    }

    private Node<K,V> node(K key1) {
    
    
        Node<K, V> root = table[index(key1)];
        return root == null ? null : node(root, key1);
    }

    private Node<K,V> node(Node<K,V> node, K key1) {
    
    
        int hashCode1 =hash(key1);
        Node<K,V> result = null;
        int cmp = 0;
        while (node != null){
    
    
            int hashCode2 = node.hashCode;
            K key2 = node.key;
            // 先比较哈希值
            if (hashCode1 > hashCode2){
    
    
                node = node.right;
            }else if (hashCode1 < hashCode2){
    
    
                node = node.left;
            }else if (Objects.equals(key1, key2)){
    
    
                return node;
            }else if (key1 != null && key2 != null && key1.getClass() == key2.getClass() &&
                      key1 instanceof Comparable && (cmp = ((Comparable) key1).compareTo(key2)) != 0
            ){
    
    
                // cmp = 0只能说明大小相等,对象不一定相等
                node = cmp > 0 ? node.right : node.left;
            // 哈希值不相等,不具备可比较性,也不equals
            }else if (node.right != null && (result = node(node.right,key1)) != null) {
    
    
                return result;
            }else {
    
     // 只能往左
                node = node.left;
            }
//            }else if (node.left != null && (result = node(node.left,key1)) != null){
    
    
//                return result;
//            }else {
    
    
//                return null;
//            }
        }
        return null;
    }

    private V remove(Node<K,V> node) {
    
    
        if (node == null) return null;
        size--;
        V oldValue = node.value;
        if (node.hasTwoChildren()) {
    
    
            Node<K,V> successor = successor(node);
            node.key = successor.key;
            node.value = successor.value;
            node.hashCode = successor.hashCode;
            node = successor;
        }
        Node<K,V> replacement = node.left != null ? node.left : node.right;
        int index = index(node);
        if (replacement != null) {
    
    
            replacement.parent = node.parent;   // 更改parent
            if (node.parent == null) {
    
       // 度为1的根节点
                table[index] = replacement;
            } else if (node == node.parent.left) {
    
    
                node.parent.left = replacement;
            } else {
    
    
                node.parent.right = replacement;
            }
            afterRemove(node, replacement);
        } else if (node.parent == null) {
    
    
            table[index] = null;
        } else {
    
    
            if (node == node.parent.left) {
    
    
                node.parent.left = null;
            } else {
    
    
                node.parent.right = null;
            }
            afterRemove(node, null);
        }
        return oldValue;
    }

    private Node<K,V> successor(Node<K,V> node) {
    
    
        if (node == null) return null;
        Node<K,V> p = node.right;
        if (p != null) {
    
    
            while (p.left != null) p = p.left;
            return p;
        }
        while (node.parent != null && node == node.parent.right) {
    
    
            node = node.parent;
        }
        return node.parent;
    }

    private Node<K,V> color(Node<K,V> node, boolean color){
    
    
        if (node == null) return node;
        node.color = color;
        return node;
    }

    private Node<K,V> red(Node<K,V> node){
    
     return color(node, RED); }
    private Node<K,V> black(Node<K,V> node){
    
     return color(node, BLACK); }
    private boolean colorOf(Node<K,V> node){
    
     return node == null ? BLACK :  node.color; }
    private boolean isRed(Node<K,V> node){
    
     return colorOf(node) == RED; }
    private boolean isBlack(Node<K,V> node){
    
     return colorOf(node) == BLACK; }
}
HashMap测试用例
package com.zimo;
public class Key {
    
    
    protected int value;

    public Key(int value) {
    
    
        this.value = value;
    }

    @Override
    public int hashCode() {
    
    
        return value / 10;
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (obj == this) return true;
        if (obj == null || obj.getClass() != getClass()) return false;
        return ((Key) obj).value == value;
    }
}
package com.zimo;
public class SubKey1 extends Key {
    
    
    public SubKey1(int value) {
    
    
        super(value);
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (obj == this) return true;
        if (obj == null || (obj.getClass() != SubKey1.class && obj.getClass() != SubKey2.class)) return false;
        return ((Key) obj).value == value;
    }
}
package com.zimo;
public class SubKey2 extends Key {
    
    
    public SubKey2(int value) {
    
    
        super(value);
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (obj == this) return true;
        if (obj == null || (obj.getClass() != SubKey1.class && obj.getClass() != SubKey2.class)) return false;
        return ((Key) obj).value == value;
    }
}
package com.zimo;
import com.zimo.哈希表.MyHashMap;
import java.util.HashMap;
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MyHashMap<Object, Integer> map = new MyHashMap<>();
//        test1();
//        test2(map);
//        test3(map);
//        test4(map);
//        test5(map);
    }
    private static void test1(){
    
    
        SubKey1 subKey1 = new SubKey1(1);
        SubKey2 subKey2 = new SubKey2(1);

        MyHashMap<Object, Integer> map = new MyHashMap<>();
        map.put(subKey1,1);
        map.put(subKey2,2);
        System.out.println(map.size());

        HashMap<Object, Integer> map1 = new HashMap<>();
        map1.put(subKey1,1);
        map1.put(subKey2,2);
        System.out.println(map1.size());
    }
    private static void test2(MyHashMap<Object, Integer> map) {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            map.put(new Key(i), i);
        }
        for (int i = 5; i <= 7; i++) {
    
    
            map.put(new Key(i), i + 5);
        }
        System.out.println(map.size() == 20);
        System.out.println(map.get(new Key(4)) == 4);
        System.out.println(map.get(new Key(5)) == 10);
        System.out.println(map.get(new Key(6)) == 11);
        System.out.println(map.get(new Key(7)) == 12);
        System.out.println(map.get(new Key(8)) == 8);
    }
    private static void test3(MyHashMap<Object, Integer> map) {
    
    
        map.put(null, 1);
        map.put(new Object(),2);
        map.put("jack", 3);
        map.put(10, 4);
        map.put(new Object(), 5);
        map.put("jack", 6);
        map.put(10, 7);
        map.put(null, 8);
        map.put (10,null);
        System.out.println(map.size() == 5);
        System.out.println(map.get(null) == 8);
        System.out.println(map.get("jack") == 6);
        System.out.println(map.get(10) == null);
        System.out.println(map.get(new Object()) == null);
        System.out.println(map.containsKey(10));
        System.out.println(map.containsKey(null));
        System.out.println(map.containsValue(null));
        System.out.println(map.containsValue(1) == false);
    }
    private static void test4(MyHashMap<Object, Integer> map) {
    
    
        map.put("jack", 1);
        map.put("rose", 2);
        map.put("jim", 3);
        map.put("jake", 4);
        for (int i = 1; i <= 10; i++) {
    
    
            map.put("test" + i, i);
            map.put(new Key(i), i);
        }
        for (int i = 5; i <= 7; i++) {
    
    
            System.out.println(map.remove(new Key(i)) == i);
        }
        for ( int i = 1; i <= 3; i++) {
    
    
            map.put(new Key(i), i + 5);
        }
        System.out.println(map.size() == 21);
        System.out.println(map.get(new Key(1)) == 6);
        System.out.println(map.get(new Key(2)) == 7);
        System.out.println(map.get(new Key(3)) == 8);
        System.out.println(map.get(new Key(4)) == 4);
        System.out.println(map.get(new Key(5)) == null);
        System.out.println(map.get(new Key(6)) == null);
        System.out.println(map.get(new Key(7)) == null);
        System.out.println(map.get(new Key(8)) == 8);
    }
    private static void test5(MyHashMap<Object, Integer> map) {
    
    
        for (int i = 1; i <=20; i++){
    
    
            map.put(new SubKey1(i), i);
        }
        map.put(new SubKey2(1),5);
        System.out.println(map.get(new SubKey1(1)) == 5);
        System.out.println(map.get(new SubKey2(1)) == 5);
        System.out.println(map.size() == 20);
    }
}
装填因子
  • 装填因子(Load Factor):节点总数量 / 哈希表桶数组长度,也叫做负载因子

  • JDK1.8的HashMap中,如果装填因子超过0.75,就扩容为原来的2倍

  • 当扩容为原来容量的2倍时,节点的索引有2种情况:

    1. 保持不变
    2. 要么index = index + 旧容量
  • 动态扩容

package com.zimo.哈希表;
import com.zimo.映射.Map;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
/**
 * 使用哈希表实现HashMap
 *      直接使用红黑树解决哈希冲突
 *          数组中直接挂红黑树的根节点,不挂红黑树对象,可以省略红黑树的成员(size,comparator...)
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/11 16:12:20
 * @version v0.2 by 2020/11/17 16:06:18    优化索引   动态扩容
 */
public class MyHashMap<K,V> implements Map<K,V> {
    
    
    private static final boolean RED = false;
    private static final boolean BLACK = true;
+   private static final int DEFAULT_CAPACITY = 1 << 4;     // 设计为2的幂次方,默认为16
+   private static final float DEFAULT_LOAD_FACTOR = 0.75f; // 装填因子
    private int size;
    private Node<K,V>[] table;

    public MyHashMap() {
    
    
        this.table = new Node[DEFAULT_CAPACITY];
    }

    @Override
    public V put(K key, V value) {
    
    
+       resize();
        // 1.拿到索引
        int index = index(key);
        ...
        return null;
    }

+   private void resize() {
    
    
+       // 装填因子 ≤ 0.75
+       if (size / table.length <= DEFAULT_LOAD_FACTOR) return;
+       Node<K, V>[] oldTable = this.table;
+       table = new Node[oldTable.length << 1];
+       // 挪动旧的数据,不要重新创建节点
+       Queue<Node<K,V>> queue = new LinkedList<>();
+       for (int i = 0; i < oldTable.length; i++) {
    
    
+           if (oldTable[i] == null) continue;
+           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;
+       newNode.color = RED;
+
+       int index = index(newNode);            // 找到新位置
+       Node<K, V> root = table[index];
+       if (root == null) {
    
                     // 如果根节点为空,直接挪过去,size不变
+           root = newNode;
+           table[index] = root;
+           afterPut(root);
+           return;
+       }
+
+       // 哈希冲突
+       Node<K, V> parent = root;
+       Node<K, V> node = root;
+       int cmp = 0;
+       K key1 = newNode.key;
+       int hashCode1 = newNode.hashCode;
+       do {
    
    
+           parent = node;
+           K key2 = node.key;
+           int hashCode2 = node.hashCode;
+           // 挪动时,不需要判断equals和搜索,因为旧的哈希表上数据不可能存在两个相同的key
+           if (hashCode1 > hashCode2){
    
    
+               cmp = 1;
+           }else if (hashCode1 < hashCode2){
    
    
+               cmp = -1;
+           }else if (key1 != null && key2 != null
+                   && key1.getClass() == key2.getClass()
+                   && key1 instanceof Comparable
+                   && (cmp = ((Comparable) key1).compareTo(key2)) != 0){
    
    
+           }else{
    
    
+               cmp = System.identityHashCode(key1) - System.identityHashCode(key2);
+           }
+           // 这里cmp不可能等0,挪动时不需要覆盖操作
+           if (cmp > 0){
    
    
+               node = node.right;
+           }else{
    
    
+               node = node.left;
+           }
+       }while (node != null);
+       newNode.parent = parent;
+       if (cmp > 0){
    
    
+           parent.right = newNode;
+       }else {
    
    
+           parent.left = newNode;
+       }
+       afterPut(newNode);
+   }
}
自定义对象作为Key
  • 自定义对象作为key,最好同时重写hashCode、equals方法
  • equals:用以判断2个key是否为同一个key
    • 自反性:对于任何非null的x,x.equals(x)必须返回true
    • 对称性:对于任何非null的x、y,如果y.equals(x)返回true,x.equals(y)必须返回true
    • 传递性:对于任何非null的x、y、z,如果x.equals(y)、y.equals(z)返回true,那么x.equals(z)必须返回true
    • 一致性:对于任何非null的x、y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true,或者一致的返回false
    • 对于任何非null的x,x.equals(null)必须返回false

HashMap VS TreeMap

  • TreeMap:
    • 元素具备可比较性且要求升序遍历(按照元素从小到大)
  • HashMap:
    • 无序遍历

LinkedHashMap

  • 在HashMap的基础上维护元素的添加顺序,使得遍历的结果是遵从添加顺序的
  • 删除度为2的节点node时
    • 需要注意更换node与前驱/后继节点的连接位置

LinkedHashMap删除细节

  • HashMap修改
public class MyHashMap<K,V> implements Map<K,V> {
    
    
    @Override
    public V put(K key, V value) {
    
    
        ...
        if (root == null) {
    
         // 新节点
*           root = createNode(key, value, null);
            table[index] = root;
            size++;
            afterPut(root);
            return null;
        }
        ...
        do {
    
    
            ...
        }while (node != null);
*       Node<K, V> newNode = createNode(key, value, parent);
        if (cmp > 0){
    
    
            parent.right = newNode;
        }else {
    
    
            parent.left = newNode;
        }
        size++;
        afterPut(newNode);
        return null;
    }
*   protected V remove(Node<K,V> node) {
    
    
        ...
    }

+   protected Node<K,V> createNode(K key, V value, Node<K,V> parent){
    
    
+       return new Node<>(key, value, parent);
+   }
    
*   private void fixAfterPut(Node<K,V> node) {
    
    }	// afterPut
*   private void fixAfterRemove(Node<K,V> node, Node<K,V> replacement) {
    
    }	// afterRemove
    
    protected V remove(Node<K,V> node) {
    
    
        ...
+       // 交给子类处理
+       afterRemove(node);
        return oldValue;
    }
}
  • LinkedHashMap实现
package com.zimo.哈希表;

import java.util.Objects;

/**
 * LinkedHashMap - 双向链表
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/17 18:48:40
 */
public class LinkedHashMap<K,V> extends MyHashMap<K,V>{
    
    
    private LinkedNode<K,V> first;
    private LinkedNode<K,V> last;

    @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;
    }

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

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

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

    @Override
    protected void afterRemove(Node<K, V> wantNode, Node<K, V> removeNode) {
    
    
        LinkedNode<K, V> linkedWantNode = (LinkedNode<K, V>) wantNode;
        LinkedNode<K, V> linkedRemoveNode = (LinkedNode<K, V>) removeNode;
        if (linkedWantNode != linkedRemoveNode){
    
        // 度为2的节点
            // 交换linkedWantNode和linkedRemoveNode在【链表】中的位置,不是红黑树的位置
            // 1.交换prev
            LinkedNode<K, V> tmp = linkedWantNode.prev;
            linkedWantNode.prev = linkedRemoveNode.prev;
            linkedRemoveNode.prev = tmp;
            if (linkedWantNode.prev == null){
    
    
                first = linkedWantNode;
            }else {
    
    
                linkedWantNode.prev.next = linkedWantNode;
            }
            if (linkedRemoveNode.prev == null){
    
    
                first = linkedRemoveNode;
            }else {
    
    
                linkedRemoveNode.prev.next = linkedRemoveNode;
            }
            // 1.交换next
            tmp = linkedWantNode.next;
            linkedWantNode.next = linkedRemoveNode.next;
            linkedRemoveNode.next = tmp;
            if (linkedWantNode.next == null){
    
    
                last = linkedWantNode;
            }else {
    
    
                linkedWantNode.next.prev = linkedWantNode;
            }
            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 {
    
    
            next.prev = prev;
        }
    }

    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);
        }
    }
}

HashSet/LinkedHashSet 实现

package com.zimo.哈希表;

import com.zimo.映射.Map;
import com.zimo.集合.Set;

/**
 * 利用HashMap实现HashSet
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/11/17 21:30
 */
public class HashSet<E> implements Set<E> {
    
    
    private MyHashMap<E, Object> map = new MyHashMap<>();			// HashSet
    private LinkedHashMap<E, Object> map = new LinkedHashMap<>();	 // LinkedHashSet 

    @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 visitor) {
    
    
        map.traversal(new Map.Visitor<E, Object>() {
    
    
            @Override
            public boolean visit(E key, Object value) {
    
    
                return visitor.visit(key);
            }
        });
    }
}

猜你喜欢

转载自blog.csdn.net/qq_38205875/article/details/109773095