散列和散列碼(1)
阿新 • • 發佈:2018-04-21
相同 ole urn put arr map this () binarys 1.1
1.2散列數據結構中鍵類的
1.3重載
1.4 instanceof關鍵字:
一.前言
1.1==
符號
==
比較的對象內存地址,也就是兩個引用指向的地址,相同即指向同一個對象則返回true。Object中的equals(Object obj)
方法默認使用的是==
方法比較兩個對象:
public boolean equals(Object obj) {
return (this == obj);
}
1.2散列數據結構中鍵類的equals()、hashCode()
方法必須重載,否則這些數據結構便不能正確處理鍵;
1.3重載equals(Object obj)
原則
重載的equals
方法需要滿足一下條件:
- 自反性:x.equals(x)返回true;
- 對稱性:x.equals(y)與y.equals(x)返回值相同;
- 傳遞性:x.equals(y)為true且y.equals(z)為true,則x.equals(y)應該為true;
- 一致性:用於比較等價的信息未變則不管調用多少次x.equals(y)一直不應該改變;
- x不為null,則x.equals(null)一定為false,否則為TRUE;
1.4 instanceof關鍵字:object instanceof ClassZ
instanceof
關鍵字會先檢查左側對象是否為null,為null則返回false;
二.基本思想
2.1散列速度
如下示例SlowMap慢的原因在於對於鍵的查詢使用的是線性查詢——線性查詢是最慢的查詢方式 。
對於鍵的查詢,保持鍵的排序而且使用Collections.binarySearch()
進行查詢解決速度問題的方案之一。
散列的思想是:數組是最快的數據結構將鍵的信息保存在數組中,鍵調用方法int hashCode()
返回的數字(即散列碼)就是數組下標。
因為Map可以保存數量不確定的值,而數組不能調整容量,因此我們允許不同鍵產生相同下標,使用“外部鏈接”解決沖突,即數組保存list,如下圖:
因此一次查詢流程是:
- 使用對象計算散列碼,然後O(1)時間找打數組下標;
- 如果找到了例如上圖下標為11的位置,則對list使用
equals()
方法進行先行的查詢,找到對象對應的確切位置,也就是我們要找的鍵信息
以上,只對很少的元素進行比較,這也是HashMap速度快的原因,簡單實現見第二塊代碼區。
package containers;
import java.util.*;
public class SlowMap<K,V> extends AbstractMap<K,V> {
private List<K> keys = new ArrayList<>();
private List<V> values=new ArrayList<>();
@Override
public V put(K key, V value) {
V oldValue=get(key);//如果key不存在則返回null;
if(!keys.contains(key)){//不存在則添加
keys.add(key);
values.add(value);
}else {//存在著覆蓋
values.set(keys.indexOf(key), value);
}
return oldValue;
}
@Override
public V get(Object key){
if(!keys.contains(key)) {//不存在則返回null
return null;
}
return values.get(keys.indexOf(key));// fixme 第幾個key就返回第幾個key,不管key的具體內容;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K,V>> set=new HashSet<>();
Iterator<K> ki=keys.iterator();
Iterator<V> vi=values.iterator();
while (ki.hasNext()){
set.add(new SimpleEntry<K, V>(ki.next(),vi.next()));
}
return set;
}
}
HashMap的簡單實現
package containers;
import java.util.*;
/**
* @author 杜艮魁
* @date 2018/1/25
*/
public class SimpleHashMap<K,V> extends AbstractMap<K,V>{
//為hash_table設置一個初始值:通常使用質數來保證散列均勻
static final int SIZE=997;
//存放鍵值對,但是key決定存放位置:buckets是數組,而且元素時鏈表
LinkedList<SimpleEntry<K,V>> buckets[]=new LinkedList[SIZE];
@SuppressWarnings("unchecked")
@Override
public V put(K key, V value) {
V oldValue=null;
int index=Math.abs(key.hashCode())%SIZE;//fixme key值hashCode信息在數組中存放位置,也就是數組下標
if(buckets[index]==null)//如果數組這個位置鏈表沒有節點,則對這個位置頭節點進行初始化
buckets[index]=new LinkedList<>();
LinkedList<SimpleEntry<K,V>> bucket=buckets[index];//bucket指向元素要存放的數組下標對應的頭結點
SimpleEntry<K,V> pair=new SimpleEntry<K, V>(key,value);//要存放的鍵值對放進數據存放形式的元素裏
boolean found=false;//數據是否已經出現在了Map中
ListIterator<SimpleEntry<K,V>> it=bucket.listIterator();//遍歷鍵對應的數組位置鏈表,將其放進list中
while(it.hasNext()){//遍歷這個位置的list
SimpleEntry<K,V> iPair=it.next();
if(iPair.getKey().equals(key)){//如果鍵equals相等則新值替代舊值,則—之前通過找數組下標的hashCode()已經相等
oldValue=iPair.getValue();
it.set(pair);//用新值代替舊值
found=true;
break;
}
}
if(!found){//如果沒有找到,則添加到末尾
buckets[index].add(pair);
}
return oldValue;
}
@Override
public V get(Object key) {//註意參數不是K
int index=Math.abs(key.hashCode())%SIZE;
if(buckets[index]==null) return null;//如果定位到的鏈表為null,則返回null
for (SimpleEntry<K,V> iPair:buckets[index])//否則遍歷這個鏈表,比較key值
if(iPair.getKey().equals(key))
return iPair.getValue();
return null;
}
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K,V>> set=new HashSet<>();
for (LinkedList<SimpleEntry<K,V>>bucket:buckets) {//定位數組位置
if(bucket==null) continue;//如果這個數組所在
for (SimpleEntry<K,V> mpair:bucket)//不為空的話則遍歷裏邊元素放進set
set.add(mpair);
}
return set;
}
}
散列和散列碼(1)