package test;
import java.util.*;
public class SimpleHashMap<K, V> extends AbstractMap<K, V> {
// 連結串列陣列長度
static final int SIZE = 997;
/**
* 建立一個連結串列陣列
* 連結串列裡面存放的是 MapEntry<K, V>
* 每個 buckets[i] 都對應一個LinkedList<MapEntry<K, V>>
* 雖然不能建立泛型陣列,但是可以建立泛型陣列的引用
*/
@SuppressWarnings("unchecked")
private LinkedList<Node<K, V>>[] buckets = new LinkedList[SIZE];
/**
* 將鍵值對放入 HashMap
*
* @param key 鍵
* @param value 值
* @return
*/
@Override
public V put(K key, V value) {
V oldValue = null;
// 這裡就是通過對鍵雜湊然後取餘
// 它代表了物件(LinkedList-->具有相同的索引)在陣列裡的位置,既陣列下標
int index = Math.abs(key.hashCode()) % SIZE;
if (buckets[index] == null) {
// 如果是第一次雜湊到這個陣列下標,那麼就生成一個新的 LinkedList,連結串列裡面儲存的是 MapEntry<K,V>
buckets[index] = new LinkedList<>();
}
LinkedList<Node<K, V>> bucket = buckets[index];
// 根據鍵值對構建 hashMap 裡儲存的結點
Node<K, V> node = new Node<>(key, value);
boolean found = false;
ListIterator<Node<K, V>> it = bucket.listIterator();
while (it.hasNext()) {
Node<K, V> iPair = it.next();
if (iPair.getKey().equals(key)) {
// 如果存在舊的鍵值對,就用新的 value 代替舊的 value; key 保持不變
oldValue = iPair.getValue();
it.set(node);
found = true;
break;
}
}
if (!found) {
// 如果是一個新的鍵值,那麼直接新增到這個 LinkedList 中
buckets[index].add(node);
}
return oldValue;
}
/**
* 根據鍵返回value
*
* @param key 鍵
* @return 存在該 key 則返回相應的 value;否則返回 null
*/
@Override
public V get(Object key) {
// 獲得相應的 LinkedList 對應的索引
// 為什麼要用 LinkedList: 因為 hashcode 方法產生的雜湊碼不能完全確定一個物件(發生碰撞),即不同的物件雜湊到同一個陣列下標
// 解決辦法: 定義一個 List 把發生碰撞的物件放入其中,然後執行線性查詢
// hashcode()相等的兩個物件,equals()不一定相等;而equals()相等的兩個物件,hashcode()一定相等
int index = Math.abs(key.hashCode()) % SIZE;
if (buckets[index] == null) {
return null;
}
for (Node<K, V> node : buckets[index]) {
if (node.getKey().equals(key)) {
return node.getValue();
}
}
return null;
}
/**
* @return 返回鍵值對所對應的 Set
*/
@Override
public Set<Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> set = new HashSet<>();
for (LinkedList<Node<K, V>> bucket : buckets) {
if (bucket == null) continue;
set.addAll(bucket);
}
return set;
}
/**
* 內部儲存的結點
*
* @param <K> 鍵
* @param <V> 值
*/
static class Node<K, V> implements Map.Entry<K, V> {
private K key;
private V value;
Node(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
return Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue());
}
return false;
}
@Override
public String toString() {
return key + "=" + value;
}
}
public static void main(String[] args) {
SimpleHashMap<String, String> m = new SimpleHashMap<>();
m.put("Aa", "AAAAA");
m.put("BB", "BBBBB");
m.put("CC", "CCCCC");
System.out.println(m);
System.out.println(m.get("Aa"));
System.out.println(m.entrySet());
}
}
複製程式碼
hashCode()
的作用:提高查詢效率(使用O(1)的時間來縮小查詢範圍)
故基於雜湊的Map和Set儲存的物件都要是實現 hashCode()
。
一般的hashCode()
與 equals()
需要同時實現。
並且要滿足
hashcode()相等的兩個物件,equals()不一定相等;而equals()相等的兩個物件,hashcode()一定相等
雖然不是強制的規範,但是一般的實現都要滿足這個條件!
似乎有種似曾相識的感覺?
沒錯 switch 對 String 的處理就是先通過比較hashCode()
返回值,然後再用equals()
比較。
public class TestString {
public static void main(String[] args) {
String str = "world";
switch (str) {
case "hello":
System.out.println("hello");
break;
case "world":
System.out.println("world");
break;
default: break;
}
}
}
複製程式碼
反編譯後:
public static void main(String args[]) {
String str = "world";
String s;
switch((s = str).hashCode()) {
case 99162322:
if(s.equals("hello"))
System.out.println("hello");
break;
case 113318802:
if(s.equals("world"))
System.out.println("world");
break;
default: break;
}
}
複製程式碼