package com.cskaoyan.hashmap;
import java.util.LinkedHashSet;
import java.util.Set;
/*
API:
// void put(K key, V value)
// V get(K key)
// void delete(K key)
// void clear()
// boolean contains(K key)
// boolean isEmpty()
// int size()
// Set<K> keys()
*/
public class MyHashMap<K, V> {
// 常量
private static final int DEFAULT_ARRAY_SIZE = 16;
private static final int MAX_ARRAY_SIZE = 1 << 30;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 属性
private Entry[] table;
private int size;
private float loadFactor;
private int threshold; //阈值, 当size达到threshold时候就需要扩容了
private static class Entry {
Object key;
Object value;
int hash;
Entry next;
public Entry(Object key, Object value, int hash, Entry next) {
this.key = key;
this.value = value;
this.hash = hash;
this.next = next;
}
@Override
public String toString() {
return key + "=" + value;
}
}
// 构造方法
public MyHashMap() {
this(DEFAULT_ARRAY_SIZE, DEFAULT_LOAD_FACTOR);
}
public MyHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public MyHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity <= 0 || initialCapacity > MAX_ARRAY_SIZE) {
throw new IllegalArgumentException("initialCapacity=" + initialCapacity);
}
if (loadFactor <= 0) {
throw new IllegalArgumentException("loadFactor=" + loadFactor);
}
// 求大于等于initialCapacity最小的2的幂
int capacity = calculateCapacity(initialCapacity);
table = new Entry[capacity];
this.loadFactor = loadFactor;
threshold = (int) (table.length * loadFactor);
}
private int calculateCapacity(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return n + 1;
}
/**
* 添加键值对,如果键已经存在,则更新它关联的值
*
* @param key 键
* @param value 值
* @return 原来的值,如果key不存在返回 null
*/
@SuppressWarnings("unchecked")
public V put(K key, V value) {
if (key == null || value == null) {
throw new IllegalArgumentException("Key or value cannot be null.");
}
int hash = hash(key);
int index = indexFor(hash, table.length);
// 遍历链表, 查找key
for (Entry e = table[index]; e != null; e = e.next) {
if (hash == e.hash && (key == e.key || key.equals(e.key))) {
// 更新值,并把原来的值返回
V oldValue = (V) e.value;
e.value = value;
return oldValue;
}
}
addEntry(key, value, hash, index);
return null;
}
private void addEntry(K key, V value, int hash, int index) {
// 判断是否需要扩容
if (size >= threshold) {
if (table.length == MAX_ARRAY_SIZE) {
// 破坏加载因子的限制
threshold = Integer.MAX_VALUE;
} else {
// 扩容
grow(table.length << 1);
// 重新计算索引值
index = indexFor(hash, table.length);
}
}
// 添加结点(头插法)
Entry e = new Entry(key, value, hash, table[index]);
table[index] = e;
size++;
}
private void grow(int newCapacity) {
Entry[] newTable = new Entry[newCapacity];
// 重新散列
for (Entry e : table) {
// 遍历链表,重新散列
while (e != null) {
// 保存e的next结点
Entry next = e.next;
// 计算在新数组中索引
int i = indexFor(e.hash, newCapacity);
// 头插法
e.next = newTable[i];
newTable[i] = e;
// e指向原链表的下一个结点
e = next;
}
}
table = newTable;
// 更新阈值
threshold = (int) (newCapacity * loadFactor);
}
/**
* 通过键获取值
*
* @param key 键
* @return 键关联的值,如果键不存在返回null
*/
@SuppressWarnings("unchecked")
public V get(K key) {
if (key == null) throw new IllegalArgumentException("Key cannot be null.");
int hash = hash(key);
int index = indexFor(hash, table.length);
// 遍历链表, 找到key关联值
for (Entry e = table[index]; e != null; e = e.next) {
if (hash == e.hash && (key == e.key || key.equals(e.key))) {
// 找到该结点
return (V) e.value;
}
}
// 没有这样的key
return null;
}
private int indexFor(int hash, int length) {
return hash & (length - 1);
}
private int hash(K key) {
int h = key.hashCode();
return (h << 16) ^ (h >> 16);
}
/**
* 判断key是否在哈希表中存在
*
* @param key 键
* @return 如果存在返回true, 否则返回false
*/
public boolean contains(K key) {
return get(key) != null;
}
/**
* 删除键值对
*
* @param key 键
* @return 被删除的值, 如果不存在则返回null
*/
@SuppressWarnings("unchecked")
public V delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null.");
}
int hash = hash(key);
int index = indexFor(hash, table.length);
// 遍历链表, 找到key关联值
for (Entry e = table[index], p = null; e != null; p = e, e = e.next) {
if (hash == e.hash && (key == e.key || key.equals(e.key))) {
// 删除结点
V deleteValue = (V) e.value;
if (p == null) table[index] = e.next;
else p.next = e.next;
size--;
return deleteValue;
}
}
return null;
}
/**
* 清空所有键值对
*/
public void clear() {
for(int i = 0; i < table.length; i++) {
table[i] = null;
}
size = 0;
}
/**
* 获取链表中键值对的个数
*
* @return 链表中键值对的个数
*/
public int size() {
return size;
}
/**
* 判断哈希表是否为空
*
* @return 如果哈希表为空返回true, 否则返回false
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 获取键的集合
*
* @return 键的集合
*/
@SuppressWarnings("unchecked")
public Set<K> keys() {
Set<K> set = new LinkedHashSet<>();
// 遍历哈希表
for (Entry e : table) {
while (e != null) {
set.add((K) e.key);
e = e.next;
}
}
return set;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
for (Entry e : table) {
while (e != null) {
sb.append(e).append(", ");
e = e.next;
}
}
if (!isEmpty()) sb.delete(sb.length() - 2, sb.length());
return sb.append("}").toString();
}
public static void main(String[] args) {
// V put(K key, V value)
MyHashMap<Character, String> map = new MyHashMap<>();
// System.out.println(map);
map.put('A', "Allen");
map.put('B', "Beyonce");
map.put('C', "Catalina");
map.put('D', "Diana");
// System.out.println(map);
/*System.out.println(map.put('A', "Adele"));
System.out.println(map);*/
// boolean contains(K key), V get(K key)
/*System.out.println(map.get('B'));
System.out.println(map.get('X'));
System.out.println(map.contains('B'));
System.out.println(map.contains('X'));*/
// V delete(K key)
/*System.out.println(map.delete('C'));
System.out.println(map);
System.out.println(map.size());
System.out.println(map.delete('X'));
System.out.println(map);
System.out.println(map.size());*/
// int size(), void clear(), boolean isEmpty()
/*System.out.println(map);
System.out.println(map.size());
System.out.println(map.isEmpty());
map.clear();
System.out.println(map);
System.out.println(map.size());
System.out.println(map.isEmpty());*/
// Set<K> keys()
Set<Character> keys = map.keys();
for(char key : keys) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
}
}