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的元素串起来
- 开放定址法(Open Addressing)
JDK1.8的哈希冲突解决方案
Java 官方使用链地址法,默认使用单向链表将元素串起来,在添加元素时,可能会由单向链表转为红黑树来存储元素
- 比如当哈希表容量 ≥ 64 且单向链表的节点数量大于8时
- 当红黑树节点数量少到一定程度时,又会转为单向链表
JDK1.8中的哈希表是使用链表 + 红黑树解决哈希冲突
- 为何要使用单向链表?
- 每次都要从头节点开始遍历
- 单向链表比双向链表少一个指针,可以节省内存空间
哈希函数
-
哈希表中哈希函数的大致实现步骤
-
先生成key的哈希值(必须是整数)
-
再让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,它是个奇素数(既是奇数,又是素数,也就是质数)
- 素数和其他数相乘的结果比其他方式更容易产成唯一性,减少哈希冲突
- 31是一个奇素数,JVM会将31 * i优化成(i << 5) - i
-
自定义对象
- 默认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种情况:
- 保持不变
- 要么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与前驱/后继节点的连接位置
- 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);
}
});
}
}