淺談安卓和iOS使用的hash

前言

哈希(Hash)或者說散列表呼寸,它是一種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)控妻。Hash 表是一種特殊的數(shù)據(jù)結(jié)構(gòu)鸥印,它同數(shù)組、鏈表以及二叉排序樹等相比較有很明顯的區(qū)別蹬音,但它又是是數(shù)組和鏈表的基礎(chǔ)上演化而來上煤,既具有數(shù)組的有點(diǎn),又具有鏈表的有點(diǎn)著淆。能夠快速定位到想要查找的記錄劫狠,而不是與表中存在的記錄的關(guān)鍵字進(jìn)行比較來進(jìn)行查找。應(yīng)用了函數(shù)映射的思想將記錄的存儲(chǔ)位置與記錄的關(guān)鍵字關(guān)聯(lián)起來永部,從而能夠很快速地進(jìn)行查找独泞。

哈希表定義

哈希表(hash table,也叫散列表)苔埋,是根據(jù)鍵(key)直接訪問訪問在內(nèi)存儲(chǔ)存位置的數(shù)據(jù)結(jié)構(gòu)懦砂。

哈希表本質(zhì)是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素成為一個(gè)箱子组橄,箱子中存放的是鍵值對(duì)孕惜。根據(jù)下標(biāo)index從數(shù)組中取value。關(guān)鍵是如何獲取index晨炕,這就需要一個(gè)固定的函數(shù)(哈希函數(shù)),將key轉(zhuǎn)換成index毫炉。不論哈希函數(shù)設(shè)計(jì)的如何完美瓮栗,都可能出現(xiàn)不同的key經(jīng)過hash處理后得到相同的hash值,這時(shí)候就需要處理哈希沖突瞄勾。

哈希表優(yōu)缺點(diǎn)

優(yōu)點(diǎn) :哈希表可以提供快速的操作费奸。

缺點(diǎn) :哈希表通常是基于數(shù)組的,數(shù)組創(chuàng)建后難于擴(kuò)展进陡。也沒有一種簡(jiǎn)便的方法可以以任何一種順序〔例如從小到大)遍歷表中的數(shù)據(jù)項(xiàng)愿阐。

哈希表,典型的【空間換時(shí)間】

它是如何實(shí)現(xiàn)高效處理數(shù)據(jù)的? put("kwok", 121); put("steve",23242 ); put("jobs", 2132); 添加趾疚、搜索缨历、刪除的流程都是類似的

  1. 利用哈希函數(shù)生成key對(duì)應(yīng)的index【O(1)】

  2. 根據(jù)index操作定位數(shù)組元素【O(1)】

image-20200422160109703.png

哈希函數(shù)

哈希表中哈希函數(shù)的實(shí)現(xiàn)步驟大概如下

  1. 先生成key的哈希值(必須是整數(shù))

  2. 再讓key的哈希值跟數(shù)組的大小進(jìn)行相關(guān)運(yùn)算以蕴,生成一個(gè)索引值

func HashCode(key string) int  {
    return hashCode(key) % len(table)
}

為了提高效率,可以使用 & 位運(yùn)算取代 % 運(yùn)算【前提:將數(shù)組的長(zhǎng)度設(shè)計(jì)為 2 的冪(2n)】

func HashCode(key string) int  {
   return hashCode(key) & (len(table) -1)
}

良好的哈希函數(shù)
讓哈希值更加均勻分布 → 減少哈希沖突次數(shù) → 提升哈希表的性能

如何生成key的哈希值

key 的常見種類可能有
整數(shù)辛孵、浮點(diǎn)數(shù)丛肮、字符串、自定義對(duì)象
不同種類的 key魄缚,哈希值的生成方式不一樣宝与,但目標(biāo)是一致的
盡量讓每個(gè) key 的哈希值是唯一的
盡量讓 key 的所有信息參與運(yùn)算

在Java中,HashMap 的 key 必須實(shí)現(xiàn) hashCode冶匹、equals 方法习劫,也允許 key 為 null

Integer hash 值就是值的本身

/**
 * Returns a hash code for this {@code Integer}.
 *
 * @return  a hash code value for this object, equal to the
 *  primitive {@code int} value represented by this
 *  {@code Integer} object.
 */
@Override
public int hashCode() {
return Integer.hashCode(value);
}

Float 將存儲(chǔ)的二進(jìn)制格式轉(zhuǎn)為整數(shù)值

/**
 * Returns a hash code for this {@code Float} object. The
 * result is the integer bit representation, exactly as produced
 * by the method {@link #floatToIntBits(float)}, of the primitive
 * {@code float} value represented by this {@code Float}
 * object.
 *
 * @return a hash code value for this object.
 */
@Override
public int hashCode() {
return Float.hashCode(value);
}
/**
 * Returns a hash code for a {@code float} value; compatible with
 * {@code Float.hashCode()}.
 *
 * @param value the value to hash
 * @return a hash code value for a {@code float} value.
 * @since 1.8
 */
public static int hashCode(float value) {
return floatToIntBits(value);
}

double 和long 哈希值

/**
 * Returns a hash code for a {@code long} value; compatible with
 * {@code Long.hashCode()}.
 *
 * @param value the value to hash
 * @return a hash code value for a {@code long} value.
 * @since 1.8
 */
public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}

/**
 * Returns a hash code for a {@code double} value; compatible with
 * {@code Double.hashCode()}.
 *
 * @param value the value to hash
 * @return a hash code value for a {@code double} value.
 * @since 1.8
 */
public static int hashCode(double value) {
long bits = doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}

>> 和 ^ 的作用是?
高32bit 和 低32bit 混合計(jì)算出 32bit 的哈希值
充分利用所有信息計(jì)算出哈希值


image-20200422171256111.png

字符串hash 值

整數(shù) 5489 是如何計(jì)算出來的?
5*10^3 +4*10^2 +8*10^1 +9*10^0
字符串是由若干個(gè)字符組成的
比如字符串 jack,由 s嚼隘、t诽里、e、v嗓蘑、e 五個(gè)字符組成(字符的本質(zhì)就是一個(gè)整數(shù)) 因此须肆,steve的哈希值可以表示為s*n^4 + t*n^3 +e*n^2 +v*n^1 +e*n^0,等價(jià)于[(j*n+a)

public int hashCode() {
    int h = hash;
    final int len = length();
    if (h == 0 && len > 0) {
        for (int i = 0; i < len; i++) {
            h = 31 * h + charAt(i);//31 是一個(gè)奇素?cái)?shù),JVM會(huì)將 31 * i 優(yōu)化成 (i << 5) – i
        }
        hash = h;
    }
    return h;
}
public int hashCode() {
    int h = hash;
    final int len = length();
    if (h == 0 && len > 0) {
        for (int i = 0; i < len; i++) {
            h = (h << 5) -h  + charAt(i);
        }
        hash = h;
    }
    return h;
}

自定義對(duì)象的hash值

package main

import "fmt"

type MyString string
type MyInt int
type Student struct {
   Name    MyString
   Age     MyInt
   ID      MyString
   Address MyString
}

func (s MyString) hashCode() int {
   h := 0
   for _, c := range string(s) {
      h = (h << 16) - h + int(c)
   }
   return h
}

func (i MyInt) hashCode() int {
   return int(i)
}
func (s Student) hashCode() int {
   hash := 0
   hash = (hash << 16) - hash + s.Name.hashCode()
   hash = (hash << 16) - hash + s.Age.hashCode()
   hash = (hash << 16) - hash + s.ID.hashCode()
   hash = (hash << 16) - hash + s.Address.hashCode()
   return hash
}
func main() {
   s1 := Student{
      Name: "steve",
      Age: 23,
      Address: "北京市海淀區(qū)",
      ID: "2011234010303",
   }
   fmt.Println(s1.hashCode())

}

自定義對(duì)象作為key

自定義對(duì)象作為 key蚀狰,最好同時(shí)重寫 hashCode 借卧、equals 方法
equals :用以判斷 2 個(gè) key 是否為同一個(gè) key

  1. 自反性:對(duì)于任何非null 的x,x.equals(x)必須返回true
  2. 對(duì)稱性:對(duì)于任何非 null 的 x拒贱、y,如果 y.equals(x) 返回 true佛嬉,x.equals(y) 必須返回 true
  3. 傳遞性:對(duì)于任何非 null 的 x逻澳、y、z暖呕,如果 x.equals(y)斜做、y.equals(z) 返回 true,那么x.equals(z) 必須 返回 true
  4. 一致性:對(duì)于任何非 null 的 x湾揽、y瓤逼,只要 equals 的比較操作在對(duì)象中所用的信息沒有被修改,多次調(diào)用 x.equals(y) 就會(huì)一致地返回 true库物,或者一致地返回 false
  • 對(duì)于任何非 null 的 x霸旗,x.equals(null) 必須返回 false
  • hashCode :必須保證 equals 為 true 的 2 個(gè) key 的哈希值一樣
  • 反過來 hashCode 相等的 key,不一定 equals 為 true

不重寫 hashCode 方法只重寫 equals 會(huì)有什么后果?
可能會(huì)導(dǎo)致 2 個(gè) equals 為 true 的 key 同時(shí)存在哈希表中

iOS 中的哈希值使用 (- (BOOL)isEqual:(id)object; @property** (readonly) NSUInteger hash; ) 待續(xù)

安卓中哈希值使用(主要是講解java 中hash map 源碼)待續(xù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戚揭,一起剝皮案震驚了整個(gè)濱河市诱告,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌民晒,老刑警劉巖精居,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锄禽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡箱蟆,警方通過查閱死者的電腦和手機(jī)沟绪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來空猜,“玉大人绽慈,你說我怎么就攤上這事”蔡海” “怎么了坝疼?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)谆沃。 經(jīng)常有香客問我钝凶,道長(zhǎng),這世上最難降的妖魔是什么唁影? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任耕陷,我火速辦了婚禮,結(jié)果婚禮上据沈,老公的妹妹穿的比我還像新娘哟沫。我一直安慰自己,他們只是感情好锌介,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布嗜诀。 她就那樣靜靜地躺著,像睡著了一般孔祸。 火紅的嫁衣襯著肌膚如雪隆敢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天崔慧,我揣著相機(jī)與錄音拂蝎,去河邊找鬼。 笑死惶室,一個(gè)胖子當(dāng)著我的面吹牛匣屡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拇涤,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼誉结!你這毒婦竟也來了鹅士?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤惩坑,失蹤者是張志新(化名)和其女友劉穎掉盅,沒想到半個(gè)月后也拜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趾痘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年慢哈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片永票。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡卵贱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侣集,到底是詐尸還是另有隱情键俱,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布世分,位于F島的核電站编振,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏臭埋。R本人自食惡果不足惜踪央,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓢阴。 院中可真熱鬧畅蹂,春花似錦、人聲如沸炫掐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)募胃。三九已至旗唁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痹束,已是汗流浹背检疫。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祷嘶,地道東北人屎媳。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像论巍,于是被迫代替她去往敵國(guó)和親烛谊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Hash表也叫散列表,是一張非常重要的數(shù)據(jù)結(jié)構(gòu),很多緩存技術(shù)的核心就是在內(nèi)存中維護(hù)一張大的Hash表 簡(jiǎn)單回顧其他...
    Mr_Guo_Coding閱讀 2,127評(píng)論 0 3
  • TreeMap分析 時(shí)間復(fù)雜度(平均)添加嘉汰,刪除丹禀,搜索:O(logn) 特點(diǎn)Key必須具備可比較性元素的分布是有序...
    ducktobey閱讀 499評(píng)論 0 0
  • Java所有類的父類Object類擁有如下重要的方法: equals 方法 和 == 在比較對(duì)象時(shí),操作符 == ...
    mrjunwang閱讀 328評(píng)論 0 0
  • equals()和hashCode()區(qū)別持搜? equals():反映的是對(duì)象或變量具體的值,即兩個(gè)對(duì)象里面包含的值...
    半路和尚怎么出家閱讀 528評(píng)論 0 2
  • 以下是我看到的一篇好文章焙矛,摘抄過來葫盼。的確可以使人對(duì)于HashMap有更深的理解。理論看完了村斟,接下來就要看一下源碼實(shí)...
    willcoder閱讀 179評(píng)論 0 1