真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

哈希表鏈表Java代碼 鏈表 Java

JAVA創(chuàng)建一個(gè)哈希表儲(chǔ)存數(shù)據(jù)并輸出,要完整代碼

我就不寫(xiě)了,給個(gè)提示吧:

成都創(chuàng)新互聯(lián)于2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元科爾沁左翼做網(wǎng)站,已為上家服務(wù),為科爾沁左翼各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108

建一個(gè)類,名字就叫員工,它有三個(gè)屬性,分別是你要的三個(gè)數(shù)據(jù),名字、工齡、工號(hào)。然后,每次put的時(shí)候這樣:put('1234',員工1);以員工工號(hào)為key,類員工為value

Java編程中,HashMap是一種什么樣的Map?

java數(shù)據(jù)結(jié)構(gòu)-HashMap

一直以來(lái)似乎都有一個(gè)錯(cuò)覺(jué),認(rèn)為map跟其他的集合類一樣繼承自Collection,其實(shí)不然,Map和Collection在結(jié)構(gòu)層次上是沒(méi)有任何關(guān)系的,通過(guò)查看源碼可以發(fā)現(xiàn)map所有操作都是基于key-value對(duì),而不是單獨(dú)的元素。

下面以HashMap為例子,深入對(duì)Map的實(shí)現(xiàn)機(jī)制進(jìn)行了解,在這個(gè)過(guò)程中,請(qǐng)打開(kāi)jdk源碼。

Hash算法

HashMap使用Hash算法,所以在解剖HashMap之間,需要先簡(jiǎn)單的了解Hash算法,Hash算法一般也成為散列算法,通過(guò)散列算法將任意的值轉(zhuǎn)化成固定的長(zhǎng)度輸出,該輸出就是散列值,這是一種壓縮映射,也就是,散列值的空間遠(yuǎn)遠(yuǎn)小于輸入的值空間。

簡(jiǎn)單的說(shuō),hash算法的意義在于提供了一種快速存取數(shù)據(jù)的方法,它用一種算法建立鍵值與真實(shí)值之間的對(duì)應(yīng)關(guān)系,(每一個(gè)真實(shí)值只能有一個(gè)鍵值,但是一個(gè)鍵值可以對(duì)應(yīng)多個(gè)真實(shí)值),這樣可以快速在數(shù)組等里面存取數(shù)據(jù)。

下面我們建立一個(gè)HashMap,然后往里面放入12對(duì)key-value,這個(gè)HashMap的默認(rèn)數(shù)組長(zhǎng)度為16,我們的key分別存放在該數(shù)組的格子中,每個(gè)格子下面存放的元素又是以鏈表的方式存放元素。

public static void main(String[] args) {

Map map = new HashMap();

map.put("What", "chenyz");

map.put("You", "chenyz");

map.put("Don't", "chenyz");

map.put("Know", "chenyz");

map.put("About", "chenyz");

map.put("Geo", "chenyz");

map.put("APIs", "chenyz");

map.put("Can't", "chenyz");

map.put("Hurt", "chenyz");

map.put("you", "chenyz");

map.put("google", "chenyz");

map.put("map", "chenyz");

map.put("hello", "chenyz");

}

當(dāng)我們新添加一個(gè)元素時(shí),首先我們通過(guò)Hash算法計(jì)算出這個(gè)元素的Hash值的hashcode,通過(guò)這個(gè)hashcode的值,我們就可以計(jì)算出這個(gè)新元素應(yīng)該存放在這個(gè)hash表的哪個(gè)格子里面,如果這個(gè)格子中已經(jīng)存在元素,那么就把新的元素加入到已經(jīng)存在格子元素的鏈表中。

運(yùn)行上面的程序,我們對(duì)HashMap源碼進(jìn)行一點(diǎn)修改,打印出每個(gè)key對(duì)象的hash值

What--hash值:8

You--hash值:3

Don't--hash值:7

Know--hash值:13

About--hash值:11

Geo--hash值:12

APIs--hash值:1

Can't--hash值:7

Hurt--hash值:1

you--hash值:10

google--hash值:3

map--hash值:8

hello--hash值:0

計(jì)算出來(lái)的Hash值分別代表該key應(yīng)該存放在Hash表中對(duì)應(yīng)數(shù)字的格子中,如果該格子已經(jīng)有元素存在,那么該key就以鏈表的方式依次放入格子中

從上表可以看出,Hash表是線性表和鏈表的綜合所得,根據(jù)數(shù)據(jù)結(jié)構(gòu)的定義,可以得出粗劣的結(jié)論,Hash算法的存取速度要比數(shù)組差一些,但是比起單純的鏈表,在查找和存取方面卻要好多。

如果要查找一個(gè)元素時(shí),同樣的方式,通過(guò)Hash函數(shù)計(jì)算出這個(gè)元素的Hash值hashcode,然后通過(guò)這個(gè)hashcode值,直接找到跟這個(gè)hash值相對(duì)應(yīng)的線性格子,進(jìn)如該格子后,對(duì)這個(gè)格子存放的鏈表元素逐個(gè)進(jìn)行比較,直到找到對(duì)應(yīng)的hash值。

在簡(jiǎn)單了解完Hash算法后,我們打開(kāi)HashMap源碼

初始化HashMap

下面我們看看Map map = new HashMap();這段代碼究竟做了什么,發(fā)生了什么數(shù)據(jù)結(jié)構(gòu)的變化。

HashMap中幾個(gè)重要的屬性

transient Entry[] table;

用來(lái)保存key-value的對(duì)象Entry數(shù)組,也就是Hash表

transient int size;

返回HashMap的鍵值對(duì)個(gè)數(shù)

final float loadFactor;

負(fù)載因子,用來(lái)決定Entry數(shù)組是否擴(kuò)容的因子,HashMap默認(rèn)是0.75f

int threshold;

重構(gòu)因子,(capacity * load factor)負(fù)載因子與Entry[]數(shù)組容積的乘值

public class HashMapK,V

extends AbstractMapK,V

implements MapK,V, Cloneable, Serializable

{

int threshold;

final float loadFactor;

transient Entry[] table;

static final float DEFAULT_LOAD_FACTOR = 0.75f;

static final int DEFAULT_INITIAL_CAPACITY = 16;

public HashMap(int initialCapacity, float loadFactor) {

if (initialCapacity 0)

throw new IllegalArgumentException("Illegal initial capacity: " +

initialCapacity);

if (initialCapacity MAXIMUM_CAPACITY)

initialCapacity = MAXIMUM_CAPACITY;

if (loadFactor = 0 || Float.isNaN(loadFactor))

throw new IllegalArgumentException("Illegal load factor: " +

loadFactor);

// Find a power of 2 = initialCapacity

int capacity = 1;

while (capacity initialCapacity)

capacity = 1;

this.loadFactor = loadFactor;

threshold = (int)(capacity * loadFactor);

table = new Entry[capacity];

init();

}

以public HashMap(int initialCapacity, float loadFactor)構(gòu)造函數(shù)為例,另外兩個(gè)構(gòu)造函數(shù)實(shí)際上也是以同種方式來(lái)構(gòu)建HashMap.

首先是要確定hashMap的初始化的長(zhǎng)度,這里使用的策略是循環(huán)查出一個(gè)大于initialCapacity的2的次方的數(shù),例如initialCapacity的值是10,那么大于10的數(shù)是2的4次方,也就是16,capacity的值被賦予了16,那么實(shí)際上table數(shù)組的長(zhǎng)度是16,之所以采用這樣的策略來(lái)構(gòu)建Hash表的長(zhǎng)度,是因?yàn)?的次方運(yùn)算對(duì)于計(jì)算機(jī)來(lái)說(shuō)是有相當(dāng)?shù)男省?/p>

loadFactor,被稱為負(fù)載因子,HashMap的默認(rèn)負(fù)載因子是0.75f

threshold,接下來(lái)是重構(gòu)因子,由負(fù)載因子和容量的乘機(jī)組成,它表示當(dāng)HashMap元素被存放了多少個(gè)之后,需要對(duì)HashMap進(jìn)行重構(gòu)。

通過(guò)這一系列的計(jì)算和定義后,初始化Entry[] table;

put(key,value)

接下來(lái)看一對(duì)key-value是如何被存放到HashMap中:put(key,value)

public V put(K key, V value) {

if (key == null)

return putForNullKey(value);

int hash = hash(key.hashCode());

int i = indexFor(hash, table.length);

System.out.println(key+"--hash值:"+i);//這就是剛才程序打印出來(lái)的key對(duì)應(yīng)hash值

for (EntryK,V e = table[i]; e != null; e = e.next) {

Object k;

if (e.hash == hash ((k = e.key) == key || key.equals(k))) {

V oldValue = e.value;

e.value = value;

e.recordAccess(this);

return oldValue;

}

}

modCount++;

addEntry(hash, key, value, i);

return null;

}

static int hash(int h) {

h ^= (h 20) ^ (h 12);

return h ^ (h 7) ^ (h 4);

}

static int indexFor(int h, int length) {

return h (length-1);

}

這里是整個(gè)hash的關(guān)鍵,請(qǐng)打開(kāi)源碼查看一步一步查看。

hash(key.hashCode()) 計(jì)算出key的hash碼 //對(duì)于hash()的算法,這里有一篇分析很透徹的文章HashMap hash方法分析

indexFor(hash, table.length) 通過(guò)一個(gè)與算法計(jì)算出來(lái),該key應(yīng)在存放在Hash表的哪個(gè)格子中。

for (EntryK,V e = table[i]; e != null; e = e.next) 然后再遍歷table[i]格中的鏈表,判斷是否已經(jīng)存在一樣的key,如果存在一樣的key值,那么就用新的value覆蓋舊的value,并把舊的value值返回。

addEntry(hash, key, value, i) 如果經(jīng)過(guò)遍歷鏈表沒(méi)有發(fā)現(xiàn)同樣的key,那么進(jìn)行addEntry函數(shù)的操作,增加當(dāng)前key到hash表中的第i個(gè)格子中的鏈表中

void addEntry(int hash, K key, V value, int bucketIndex) {

EntryK,V e = table[bucketIndex];

table[bucketIndex] = new EntryK,V(hash, key, value, e);

if (size++ = threshold)

resize(2 * table.length);

}

EntryK,V e = table[bucketIndex]; 創(chuàng)建一個(gè)Entry對(duì)象來(lái)存放鍵值(ps:Entry對(duì)象是一個(gè)鏈表對(duì)象)

table[bucketIndex] = new EntryK,V(hash, key, value, e); 將Entry對(duì)象添加到鏈表中

if (size++ = threshold) resize(2 * table.length); 最后將size進(jìn)行自增,判斷size值是否大于重構(gòu)因子,如果大于那么就是用resize進(jìn)行擴(kuò)容重構(gòu)。

void resize(int newCapacity) {

Entry[] oldTable = table;

int oldCapacity = oldTable.length;

if (oldCapacity == MAXIMUM_CAPACITY) {

threshold = Integer.MAX_VALUE;

return;

}

Entry[] newTable = new Entry[newCapacity];

transfer(newTable);

table = newTable;

threshold = (int)(newCapacity * loadFactor);

}

這里為什么是否需要擴(kuò)容重構(gòu),其實(shí)是涉及到負(fù)載因子的性能問(wèn)題

loadFactor負(fù)載因子

上面說(shuō)過(guò)loadFactor是一個(gè)hashMap的決定性屬性,HashSet和HashMap的默認(rèn)負(fù)載因子都是0.75,它表示,如果哈希表的容量超過(guò)3/4時(shí),將自動(dòng)成倍的增加哈希表的容量,這個(gè)值是權(quán)衡了時(shí)間和空間的成本,如果負(fù)載因子較高,雖然會(huì)減少對(duì)內(nèi)存空間的需求,但也會(huì)增加查找數(shù)據(jù)的時(shí)間開(kāi)銷,無(wú)論是put()和get()都涉及到對(duì)數(shù)據(jù)進(jìn)行查找的動(dòng)作,所以負(fù)載因子是不適宜設(shè)置過(guò)高

get(key)

接下來(lái)看看get(key)做了什么

public V get(Object key) {

if (key == null)

return getForNullKey();

int hash = hash(key.hashCode());

for (EntryK,V e = table[indexFor(hash, table.length)];

e != null;

e = e.next) {

Object k;

if (e.hash == hash ((k = e.key) == key || key.equals(k)))

return e.value;

}

return null;

}

這些動(dòng)作似乎是跟put(key,value)相識(shí),通過(guò)hash算法獲取key的hash碼,再通過(guò)indexFor定位出該key存在于table的哪一個(gè)下表,獲取該下標(biāo)然后對(duì)下標(biāo)中的鏈表進(jìn)行遍歷比對(duì),如果有符合就直接返回該key的value值。

keySet()

這里還涉及另一個(gè)問(wèn)題,上面說(shuō)了HashMap是跟set沒(méi)有任何親屬關(guān)系,但map也一樣實(shí)現(xiàn)了keySet接口,下面譜析一下keySet在hashMap中是如何實(shí)現(xiàn)的,這里給出部分代碼,請(qǐng)結(jié)合源碼查看

public K next() {

return nextEntry().getKey();

}

final EntryK,V nextEntry() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

EntryK,V e = next;

if (e == null)

throw new NoSuchElementException();

if ((next = e.next) == null) {

Entry[] t = table;

while (index t.length (next = t[index++]) == null)

;

}

current = e;

return e;

}

代碼很簡(jiǎn)單,就是對(duì)每個(gè)格子里面的鏈表進(jìn)行遍歷,也正是這個(gè)原因,當(dāng)我們依次將key值put進(jìn)hashMap中,但在使用map.entrySet().iterator()進(jìn)行遍歷時(shí)候卻不是put時(shí)候的順序。

擴(kuò)容

在前面說(shuō)到put函數(shù)的時(shí)候,已經(jīng)提過(guò)了擴(kuò)容的問(wèn)題

if (size++ = threshold)

resize(2 * table.length);

這里一個(gè)是否擴(kuò)容的判斷,當(dāng)數(shù)據(jù)達(dá)到了threshold所謂的重構(gòu)因子,而不是HashMap的最大容量,就進(jìn)行擴(kuò)容。

void resize(int newCapacity) {

Entry[] oldTable = table;

int oldCapacity = oldTable.length;

if (oldCapacity == MAXIMUM_CAPACITY) {

threshold = Integer.MAX_VALUE;

return;

}

Entry[] newTable = new Entry[newCapacity];

transfer(newTable);

table = newTable;

threshold = (int)(newCapacity * loadFactor);

}

void transfer(Entry[] newTable) {

Entry[] src = table;

int newCapacity = newTable.length;

for (int j = 0; j src.length; j++) {

EntryK,V e = src[j];

if (e != null) {

src[j] = null;

do {

EntryK,V next = e.next;

int i = indexFor(e.hash, newCapacity);

e.next = newTable[i];

newTable[i] = e;

e = next;

} while (e != null);

}

}

}

transfer方法實(shí)際上是將所有的元素重新進(jìn)行一些hash,這是因?yàn)槿萘孔兓?,每個(gè)元素相對(duì)應(yīng)的hash值也會(huì)不一樣。

使用HashMap

1.不要再高并發(fā)中使用HashMap,HashMap是線程不安全,如果被多個(gè)線程共享之后,將可能發(fā)生不可預(yù)知的問(wèn)題。

2.如果數(shù)據(jù)大小事固定的,最好在初始化的時(shí)候就給HashMap一個(gè)合理的容量值,如果使用new HashMap()默認(rèn)構(gòu)造函數(shù),重構(gòu)因子的值是16*0.75=12,當(dāng)HashMap的容量超過(guò)了12后,就會(huì)進(jìn)行一系列的擴(kuò)容運(yùn)算,重建一個(gè)原來(lái)成倍的數(shù)組,并且對(duì)原來(lái)存在的元素進(jìn)行重新的hash運(yùn)算,如果你的數(shù)據(jù)是有成千上萬(wàn)的,那么你的成千上萬(wàn)的數(shù)據(jù)也要跟這你的擴(kuò)容不斷的hash,這將產(chǎn)生高額的內(nèi)存和cpu的大量開(kāi)銷。

當(dāng)然啦,HashMap的函數(shù)還有很多,不過(guò)都是基于table的鏈表進(jìn)行操作,當(dāng)然也就是hash算法,Map hashMap在平時(shí)我們的應(yīng)用非常多,最重要的是我們要對(duì)每句代碼中每塊數(shù)據(jù)結(jié)構(gòu)變化心中有數(shù)。

上面主要是參考了jdk源碼,數(shù)據(jù)結(jié)構(gòu)和一些相關(guān)資料本著好記性不如爛博客的精神記錄下來(lái),希望朋友們?nèi)绻l(fā)覺(jué)哪里不對(duì)請(qǐng)指出來(lái),虛心請(qǐng)教

我想用JAVA實(shí)現(xiàn)一個(gè)鏈表的插入~刪除等操作~ (要再界面上操作~~~ 就是我按下一個(gè)鍵能進(jìn)行插入刪除的操作的

編輯詞條JAVA容器

解釋一:

容器(Container)

Spring 提供容器功能,容器可以管理對(duì)象的生命周期、對(duì)象與對(duì)象之間的依賴關(guān)系,您可以使用一個(gè)配置文件(通常是XML),在上面定義好對(duì)象的名稱、如何產(chǎn)生(Prototype 方式或Singleton 方式)、哪個(gè)對(duì)象產(chǎn)生之后必須設(shè)定成為某個(gè)對(duì)象的屬性等,在啟動(dòng)容器之后,所有的對(duì)象都可以直接取用,不用編寫(xiě)任何一行程序代碼來(lái)產(chǎn)生對(duì)象,或是建立對(duì)象與對(duì)象之間的依賴關(guān)系。

換個(gè)更直白點(diǎn)的說(shuō)明方式:容器是一個(gè)Java 所編寫(xiě)的程序,原先必須自行編寫(xiě)程序以管理對(duì)象關(guān)系,現(xiàn)在容器都會(huì)自動(dòng)幫您作好。

常用容器:WebSphere,WebLogic,Resin,Tomcat

----------------------------------

解釋二:

容器類

Java容器類包含List、ArrayList、Vector及map、HashTable、HashMap

ArrayList和HashMap是異步的,Vector和HashTable是同步的,所以Vector和HashTable是線程安全的,而 ArrayList和HashMap并不是線程安全的。因?yàn)橥叫枰ㄙM(fèi)機(jī)器時(shí)間,所以Vector和HashTable的執(zhí)行效率要低于 ArrayList和HashMap。

Collection

├List 接口

│├LinkedList 鏈表

│├ArrayList 順序結(jié)構(gòu)動(dòng)態(tài)數(shù)組類

│└Vector 向量

│ └Stack 棧

└Set

Map

├Hashtable

├HashMap

└WeakHashMap List接口

List是有序的Collection,使用此接口能夠精確的控制每個(gè)元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似于數(shù)組下標(biāo))來(lái)訪問(wèn)List中的元素,這類似于Java的數(shù)組。

和下面要提到的Set不同,List允許有相同的元素。

除了具有Collection接口必備的iterator()方法外,List還提供一個(gè)listIterator()方法,返回一個(gè) ListIterator接口,和標(biāo)準(zhǔn)的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設(shè)定元素, 還能向前或向后遍歷。

實(shí)現(xiàn)List接口的常用類有LinkedList,ArrayList,Vector和Stack。

ArrayList類

ArrayList實(shí)現(xiàn)了可變大小的數(shù)組。它允許所有元素,包括null。ArrayList沒(méi)有同步。

size,isEmpty,get,set方法運(yùn)行時(shí)間為常數(shù)。但是add方法開(kāi)銷為分?jǐn)偟某?shù),添加n個(gè)元素需要O(n)的時(shí)間。其他的方法運(yùn)行時(shí)間為線性。

每個(gè)ArrayList實(shí)例都有一個(gè)容量(Capacity),即用于存儲(chǔ)元素的數(shù)組的大小。這個(gè)容量可隨著不斷添加新元素而自動(dòng)增加,但是增長(zhǎng)算法 并沒(méi)有定義。當(dāng)需要插入大量元素時(shí),在插入前可以調(diào)用ensureCapacity方法來(lái)增加ArrayList的容量以提高插入效率。

和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。

Map接口

請(qǐng)注意,Map沒(méi)有繼承Collection接口,Map提供key到value的映射。一個(gè)Map中不能包含相同的key,每個(gè)key只能映射一個(gè) value。Map接口提供3種集合的視圖,Map的內(nèi)容可以被當(dāng)作一組key集合,一組value集合,或者一組key-value映射。

HashMap類

HashMap和Hashtable類似,不同之處在于HashMap是非同步的,并且允許null,即null value和null key。,但是將HashMap視為Collection時(shí)(values()方法可返回Collection),其迭代子操作時(shí)間開(kāi)銷和HashMap 的容量成比例。因此,如果迭代操作的性能相當(dāng)重要的話,不要將HashMap的初始化容量設(shè)得過(guò)高,或者load factor過(guò)低。

Collection接口

Collection是最基本的集合接口,一個(gè)Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。

所有實(shí)現(xiàn)Collection接口的類都必須提供兩個(gè)標(biāo)準(zhǔn)的構(gòu)造函數(shù):無(wú)參數(shù)的構(gòu)造函數(shù)用于創(chuàng)建一個(gè)空的Collection,有一個(gè)Collection參數(shù)的構(gòu)造函數(shù)用于創(chuàng)建一個(gè)新的Collection,這個(gè)新的Collection與傳入的Collection有相同的元素。后一個(gè)構(gòu)造函數(shù)允許用戶復(fù)制一個(gè)Collection。

如何遍歷Collection中的每一個(gè)元素?不論Collection的實(shí)際類型如何,它都支持一個(gè)iterator()的方法,該方法返回一個(gè)迭代子,使用該迭代子即可逐一訪問(wèn)Collection中每一個(gè)元素。典型的用法如下:

Iterator it = collection.iterator(); // 獲得一個(gè)迭代子

while(it.hasNext()) {

Object obj = it.next(); // 得到下一個(gè)元素

}

由Collection接口派生的兩個(gè)接口是List和Set。

Hashtable類

Hashtable繼承Map接口,實(shí)現(xiàn)一個(gè)key-value映射的哈希表。任何非空(non-null)的對(duì)象都可作為key或者value。

添加數(shù)據(jù)使用put(key, value),取出數(shù)據(jù)使用get(key),這兩個(gè)基本操作的時(shí)間開(kāi)銷為常數(shù)。

Hashtable通過(guò)initial capacity和load factor兩個(gè)參數(shù)調(diào)整性能。通常缺省的load factor 0.75較好地實(shí)現(xiàn)了時(shí)間和空間的均衡。增大load factor可以節(jié)省空間但相應(yīng)的查找時(shí)間將增大,這會(huì)影響像get和put這樣的操作。

使用Hashtable的簡(jiǎn)單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”:

Hashtable numbers = new Hashtable();

numbers.put(“one”, new Integer(1));

numbers.put(“two”, new Integer(2));

numbers.put(“three”, new Integer(3));

要取出一個(gè)數(shù),比如2,用相應(yīng)的key:

Integer n = (Integer)numbers.get(“two”);

System.out.println(“two = ” + n);

由于作為key的對(duì)象將通過(guò)計(jì)算其散列函數(shù)來(lái)確定與之對(duì)應(yīng)的value的位置,因此任何作為key的對(duì)象都必須實(shí)現(xiàn)hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當(dāng)作key的話,要相當(dāng)小心,按照散列函數(shù)的定義,如果兩個(gè)對(duì)象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個(gè)對(duì)象不同,則它們的hashCode不一定不同,如果兩個(gè)不同對(duì)象的hashCode相同,這種現(xiàn)象稱為沖突,沖突會(huì)導(dǎo)致操作哈希表的時(shí)間開(kāi)銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。

如果相同的對(duì)象有不同的hashCode,對(duì)哈希表的操作會(huì)出現(xiàn)意想不到的結(jié)果(期待的get方法返回null),要避免這種問(wèn)題,只需要牢記一條:要同時(shí)復(fù)寫(xiě)equals方法和hashCode方法,而不要只寫(xiě)其中一個(gè)。

Hashtable是同步的。

HashMap類

HashMap和Hashtable類似,不同之處在于HashMap是非同步的,并且允許null,即null value和null key。,但是將HashMap視為Collection時(shí)(values()方法可返回Collection),其迭代子操作時(shí)間開(kāi)銷和HashMap的容量成比例。因此,如果迭代操作的性能相當(dāng)重要的話,不要將HashMap的初始化容量設(shè)得過(guò)高,或者load factor過(guò)低。

WeakHashMap類

WeakHashMap是一種改進(jìn)的HashMap,它對(duì)key實(shí)行“弱引用”,如果一個(gè)key不再被外部所引用,那么該key可以被GC回收。

總結(jié)

如果涉及到堆棧,隊(duì)列等操作,應(yīng)該考慮用List,對(duì)于需要快速插入,刪除元素,應(yīng)該使用LinkedList,如果需要快速隨機(jī)訪問(wèn)元素,應(yīng)該使用ArrayList。

如果程序在單線程環(huán)境中,或者訪問(wèn)僅僅在一個(gè)線程中進(jìn)行,考慮非同步的類,其效率較高,如果多個(gè)線程可能同時(shí)操作一個(gè)類,應(yīng)該使用同步的類。

要特別注意對(duì)哈希表的操作,作為key的對(duì)象要正確復(fù)寫(xiě)equals和hashCode方法。

盡量返回接口而非實(shí)際的類型,如返回List而非ArrayList,這樣如果以后需要將ArrayList換成LinkedList時(shí),客戶端代碼不用改變。這就是針對(duì)抽象編程。

同步性

Vector是同步的。這個(gè)類中的一些方法保證了Vector中的對(duì)象是線程安全的。而ArrayList則是異步的,因此ArrayList中的對(duì)象并不是線程安全的。因?yàn)橥降囊髸?huì)影響執(zhí)行的效率,所以如果你不需要線程安全的集合那么使用ArrayList是一個(gè)很好的選擇,這樣可以避免由于同步帶來(lái)的不必要的性能開(kāi)銷。

數(shù)據(jù)增長(zhǎng)

從內(nèi)部實(shí)現(xiàn)機(jī)制來(lái)講ArrayList和Vector都是使用數(shù)組(Array)來(lái)控制集合中的對(duì)象。當(dāng)你向這兩種類型中增加元素的時(shí)候,如果元素的數(shù)目超出了內(nèi)部數(shù)組目前的長(zhǎng)度它們都需要擴(kuò)展內(nèi)部數(shù)組的長(zhǎng)度,Vector缺省情況下自動(dòng)增長(zhǎng)原來(lái)一倍的數(shù)組長(zhǎng)度,ArrayList是原來(lái)的50%,所以最后你獲得的這個(gè)集合所占的空間總是比你實(shí)際需要的要大。所以如果你要在集合中保存大量的數(shù)據(jù)那么使用Vector有一些優(yōu)勢(shì),因?yàn)槟憧梢酝ㄟ^(guò)設(shè)置集合的初始化大小來(lái)避免不必要的資源開(kāi)銷。

使用模式

在ArrayList和Vector中,從一個(gè)指定的位置(通過(guò)索引)查找數(shù)據(jù)或是在集合的末尾增加、移除一個(gè)元素所花費(fèi)的時(shí)間是一樣的,這個(gè)時(shí)間我們用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花費(fèi)的時(shí)間會(huì)呈線形增長(zhǎng):O(n-i),其中n代表集合中元素的個(gè)數(shù),i代表元素增加或移除元素的索引位置。為什么會(huì)這樣呢?以為在進(jìn)行上述操作的時(shí)候集合中第i和第i個(gè)元素之后的所有元素都要執(zhí)行位移的操作。這一切意味著什么呢?

這意味著,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好選擇其他的集合操作類。比如,LinkList集合類在增加或移除集合中任何位置的元素所花費(fèi)的時(shí)間都是一樣的?O(1),但它在索引一個(gè)元素的使用缺比較慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因?yàn)槟憧梢院?jiǎn)單的使用索引來(lái)代替創(chuàng)建iterator對(duì)象的操作。LinkList也會(huì)為每個(gè)插入的元素創(chuàng)建對(duì)象,所有你要明白它也會(huì)帶來(lái)額外的開(kāi)銷。

最后,在《Practical Java》一書(shū)中Peter Haggar建議使用一個(gè)簡(jiǎn)單的數(shù)組(Array)來(lái)代替Vector或ArrayList。尤其是對(duì)于執(zhí)行效率要求高的程序更應(yīng)如此。因?yàn)槭褂脭?shù)組(Array)避免了同步、額外的方法調(diào)用和不必要的重新分配空間的操作。

理解集合類

集合類存放于java.util包中。

集合類存放的都是對(duì)象的引用,而非對(duì)象本身,出于表達(dá)上的便利,我們稱集合中的對(duì)象就是指集合中對(duì)象的引用(reference)。

集合類型主要有3種:set(集)、list(列表)和map(映射)。

(1)集

集(set)是最簡(jiǎn)單的一種集合,它的對(duì)象不按特定方式排序,只是簡(jiǎn)單的把對(duì)象加入集合中,就像往口袋里放東西。

對(duì)集中成員的訪問(wèn)和操作是通過(guò)集中對(duì)象的引用進(jìn)行的,所以集中不能有重復(fù)對(duì)象。

集也有多種變體,可以實(shí)現(xiàn)排序等功能,如TreeSet,它把對(duì)象添加到集中的操作將變?yōu)榘凑漳撤N比較規(guī)則將其插入到有序的對(duì)象序列中。它實(shí)現(xiàn)的是SortedSet接口,也就是加入了對(duì)象比較的方法。通過(guò)對(duì)集中的對(duì)象迭代,我們可以得到一個(gè)升序的對(duì)象集合。

(2)列表

列表的主要特征是其對(duì)象以線性方式存儲(chǔ),沒(méi)有特定順序,只有一個(gè)開(kāi)頭和一個(gè)結(jié)尾,當(dāng)然,它與根本沒(méi)有順序的集是不同的。

列表在數(shù)據(jù)結(jié)構(gòu)中分別表現(xiàn)為:數(shù)組和向量、鏈表、堆棧、隊(duì)列。

關(guān)于實(shí)現(xiàn)列表的集合類,是我們?nèi)粘9ぷ髦薪?jīng)常用到的,將在后邊的筆記詳細(xì)介紹。

(3)映射

映射與集或列表有明顯區(qū)別,映射中每個(gè)項(xiàng)都是成對(duì)的。映射中存儲(chǔ)的每個(gè)對(duì)象都有一個(gè)相關(guān)的關(guān)鍵字(Key)對(duì)象,關(guān)鍵字決定了對(duì)象在映射中的存儲(chǔ)位置,檢索對(duì)象時(shí)必須提供相應(yīng)的關(guān)鍵字,就像在字典中查單詞一樣。關(guān)鍵字應(yīng)該是唯一的。

關(guān)鍵字本身并不能決定對(duì)象的存儲(chǔ)位置,它需要對(duì)過(guò)一種散列(hashing)技術(shù)來(lái)處理,產(chǎn)生一個(gè)被稱作散列碼(hash code)的整數(shù)值,散列碼通常用作一個(gè)偏置量,該偏置量是相對(duì)于分配給映射的內(nèi)存區(qū)域起始位置的,由此確定關(guān)鍵字/對(duì)象對(duì)的存儲(chǔ)位置。理想情況下,散列處理應(yīng)該產(chǎn)生給定范圍內(nèi)均勻分布的值,而且每個(gè)關(guān)鍵字應(yīng)得到不同的散列碼。

集合類簡(jiǎn)介

java.util中共有13個(gè)類可用于管理集合對(duì)象,它們支持集、列表或映射等集合,以下是這些類的簡(jiǎn)單介紹

集:

HashSet: 使用HashMap的一個(gè)集的實(shí)現(xiàn)。雖然集定義成無(wú)序,但必須存在某種方法能相當(dāng)高效地找到一個(gè)對(duì)象。使用一個(gè)HashMap對(duì)象實(shí)現(xiàn)集的存儲(chǔ)和檢索操作是在固定時(shí)間內(nèi)實(shí)現(xiàn)的.

TreeSet: 在集中以升序?qū)?duì)象排序的集的實(shí)現(xiàn)。這意味著從一個(gè)TreeSet對(duì)象獲得第一個(gè)迭代器將按升序提供對(duì)象。TreeSet類使用了一個(gè)TreeMap.

列表:

Vector: 實(shí)現(xiàn)一個(gè)類似數(shù)組一樣的表,自動(dòng)增加容量來(lái)容納你所需的元素。使用下標(biāo)存儲(chǔ)和檢索對(duì)象就象在一個(gè)標(biāo)準(zhǔn)的數(shù)組中一樣。你也可以用一個(gè)迭代器從一個(gè)Vector中檢索對(duì)象。Vector是唯一的同步容器類??當(dāng)兩個(gè)或多個(gè)線程同時(shí)訪問(wèn)時(shí)也是性能良好的。

Stack: 這個(gè)類從Vector派生而來(lái),并且增加了方法實(shí)現(xiàn)棧??一種后進(jìn)先出的存儲(chǔ)結(jié)構(gòu)。

LinkedList: 實(shí)現(xiàn)一個(gè)鏈表。由這個(gè)類定義的鏈表也可以像?;蜿?duì)列一樣被使用。

ArrayList: 實(shí)現(xiàn)一個(gè)數(shù)組,它的規(guī)??勺儾⑶夷芟矜湵硪粯颖辉L問(wèn)。它提供的功能類似Vector類但不同步。

映射:

HashTable: 實(shí)現(xiàn)一個(gè)映象,所有的鍵必須非空。為了能高效的工作,定義鍵的類必須實(shí)現(xiàn)hashcode()方法和equal()方法。這個(gè)類是前面java實(shí)現(xiàn)的一個(gè)繼承,并且通常能在實(shí)現(xiàn)映象的其他類中更好的使用。

HashMap: 實(shí)現(xiàn)一個(gè)映象,允許存儲(chǔ)空對(duì)象,而且允許鍵是空(由于鍵必須是唯一的,當(dāng)然只能有一個(gè))。

WeakHashMap: 實(shí)現(xiàn)這樣一個(gè)映象:通常如果一個(gè)鍵對(duì)一個(gè)對(duì)象而言不再被引用,鍵/對(duì)象對(duì)將被舍棄。這與HashMap形成對(duì)照,映象中的鍵維持鍵/對(duì)象對(duì)的生命周期,盡管使用映象的程序不再有對(duì)鍵的引用,并且因此不能檢索對(duì)象。

TreeMap: 實(shí)現(xiàn)這樣一個(gè)映象,對(duì)象是按鍵升序排列的。

Set和List都是由公共接口Collection擴(kuò)展而來(lái),所以它們都可以使用一個(gè)類型為Collection的變量來(lái)引用。這就意味著任何列表或集構(gòu)成的集合都可以用這種方式引用,只有映射類除外(但也不是完全排除在外,因?yàn)榭梢詮挠成浍@得一個(gè)列表。)所以說(shuō),把一個(gè)列表或集傳遞給方法的標(biāo)準(zhǔn)途徑是使用Collection類型的參數(shù)。

另外,站長(zhǎng)團(tuán)上有產(chǎn)品團(tuán)購(gòu),便宜有保證

數(shù)據(jù)結(jié)構(gòu)與算法-基礎(chǔ)(十八)哈希表

上期使用 紅黑樹(shù) 實(shí)現(xiàn)映射結(jié)構(gòu),這樣的結(jié)構(gòu)滿足 Key 必須具備可比性,元素有順序地分布 這兩個(gè)特點(diǎn)。在實(shí)際的應(yīng)用場(chǎng)景中,存在結(jié)構(gòu)中的 元素是不需要有序的,并且 Key 也不具備可比較性 ,哈希表完全滿足這樣的應(yīng)用場(chǎng)景。

比如設(shè)計(jì)一個(gè)公司的通訊錄,存放所有員工的通訊信息,就可以拿手機(jī)號(hào)作為 index,員工的名稱、職位等作為 value。用哈希表的方式可以將添加、刪除和搜索的時(shí)間復(fù)雜度控制在 O(1)。

這時(shí)創(chuàng)建一個(gè)數(shù)組,手機(jī)號(hào)作為 index,然后存放 value。這樣能將復(fù)雜度控制在 O(1),但是這種 空間換時(shí)間 的方式也造成了一些其他問(wèn)題,比如空間復(fù)雜度大(需要更多的空間),空間使用率極其低,非常浪費(fèi)內(nèi)存空間。

哈希表 就是空間換時(shí)間的處理方式,但是做了優(yōu)化,在空間和時(shí)間兩個(gè)緯度中達(dá)到適當(dāng)?shù)钠胶狻?/p>

哈希表也叫做散列表,整體結(jié)構(gòu)就是一個(gè)數(shù)組 ,哈希表會(huì)將 key 用哈希函數(shù)處理之后返回 hash(哈希值),hash 就是哈希表中的 index這樣的處理方式就可以滿足搜索時(shí)間是 O(1),這樣的處理方式就可以滿足搜索時(shí)間是 O(1)。因?yàn)楣1碇械?key 可能不具備可比較性,所以要做哈希處理。

在執(zhí)行哈希函數(shù)之后返回的 hash,可能會(huì)出現(xiàn)相同的情況 ,這樣的情況就是 哈希沖突 。解決哈希沖突常見(jiàn)的方法有這三種:

JDK1.8 解決哈希沖突的方式就是使用鏈地址法,其中的鏈表就是通過(guò)鏈表+紅黑樹(shù)的組合來(lái)實(shí)現(xiàn) 。比如當(dāng)哈希表中的容量大于等于 64,并且單向鏈表的節(jié)點(diǎn)數(shù)大于 8 時(shí),轉(zhuǎn)換為紅黑樹(shù),不滿足這個(gè)條件時(shí)就使用單向鏈表。

哈希函數(shù) 是生成哈希值的實(shí)現(xiàn)方法,哈希函數(shù)的實(shí)現(xiàn)步驟大致分為兩步:

hash_code 是生成哈希值的函數(shù),也可以直接用 JAVA 中的標(biāo)準(zhǔn)函數(shù) hashCode() 。

這里可以用 位運(yùn)算替換 % 運(yùn)算,來(lái)提高效率。因?yàn)? 位運(yùn)算是二進(jìn)制運(yùn)算,所以在設(shè)計(jì)數(shù)組的時(shí)候,需要將數(shù)組的長(zhǎng)度設(shè)計(jì)為 2 的冪次方。

一個(gè)良好的哈希函數(shù),可以讓生成的哈希值分布更加均勻,減少哈希沖突的次數(shù),最終可以提升哈希表的性能。

Key 的常見(jiàn)類型可能有證書(shū)、浮點(diǎn)數(shù)、字符串或者自定義對(duì)象,不同的類型生成哈希值的方式也會(huì)不一樣,但是目標(biāo)是一致的,就是 盡量讓每個(gè) Key 的哈希值唯一,盡量讓 Key 中的所有信息參與運(yùn)算 。

比如在 Java 中, Long 的哈希值實(shí)現(xiàn)如下代碼:

這里的 和 ^ 就是將高 32 bit 和低 32 bit 混合計(jì)算出 32 bit 的哈希值。

在計(jì)算字符串的哈希值時(shí),可以將字符串拆解成若干個(gè)字符,比如 jack,將它拆解成 j、a、c、k(字符的本質(zhì)就是一個(gè)整數(shù),所以 jack 的哈希值可以表示為 j * n3 + a * n2 + c * n1 + k * n0,表達(dá)式也可以寫(xiě)成 [(j * n + a) * n + c] * n + k,代碼實(shí)現(xiàn)如下:

看上面代碼時(shí),可以發(fā)現(xiàn),表達(dá)式中的 n 使用的是 31 這個(gè)數(shù)字,那么為什么用 31 呢?

因?yàn)?31 不僅符合 22 - 1 , 而且它還是個(gè)奇素?cái)?shù)(既是技術(shù),又是素?cái)?shù),還是質(zhì)數(shù)),素?cái)?shù)和其他數(shù)相乘的結(jié)果比其他方式更容易產(chǎn)生唯一性,減少哈希沖突。

JDK 中,乘數(shù) n 也是用 31,31 也是經(jīng)過(guò)觀測(cè)分布結(jié)果后的選擇,關(guān)于 31 的變體可以有以下幾種:

31 * i = (25 - 1) * i = i * 25 - i = (i 5) - i

哈希表設(shè)計(jì)的用Java代碼

#include stdio.h

#include string.h

#include stdlib.h

//#include

#define HASH_LEN 50 //哈希表的長(zhǎng)度

#define M 47

#define NAME_NO 30 //人名的個(gè)數(shù)

typedef struct NAME

{

char *py; //名字的拼音

int k; //拼音所對(duì)應(yīng)的整數(shù)

}NAME;

NAME NameList[HASH_LEN];

typedef struct hterm //哈希表

{

char *py; //名字的拼音

int k; //拼音所對(duì)應(yīng)的整數(shù)

int si; //查找長(zhǎng)度

}HASH;

HASH HashList[HASH_LEN];

/*-----------------------姓名(結(jié)構(gòu)體數(shù)組)初始化---------------------------------*/

void InitNameList()

{

NameList[0].py="chenghongxiu";

NameList[1].py="yuanhao";

NameList[2].py="yangyang";

NameList[3].py="zhanghen";

NameList[4].py="chenghongxiu";

NameList[5].py="xiaokai";

NameList[6].py="liupeng";

NameList[7].py="shenyonghai";

NameList[8].py="chengdaoquan";

NameList[9].py="ludaoqing";

NameList[10].py="gongyunxiang";

NameList[11].py="sunzhenxing";

NameList[12].py="sunrongfei";

NameList[13].py="sunminglong";

NameList[14].py="zhanghao";

NameList[15].py="tianmiao";

NameList[16].py="yaojianzhong";

NameList[17].py="yaojianqing";

NameList[18].py="yaojianhua";

NameList[19].py="yaohaifeng";

NameList[20].py="chengyanhao";

NameList[21].py="yaoqiufeng";

NameList[22].py="qianpengcheng";

NameList[23].py="yaohaifeng";

NameList[24].py="bianyan";

NameList[25].py="linglei";

NameList[26].py="fuzhonghui";

NameList[27].py="huanhaiyan";

NameList[28].py="liudianqin";

NameList[29].py="wangbinnian";

char *f;

int r,s0;

for (int i=0;iNAME_NO;i++)

{

s0=0;

f=NameList[i].py;

for (r=0;*(f+r) != NULL;r++) //方法:將字符串的各個(gè)字符所對(duì)應(yīng)的ASCII碼相加,所得的整數(shù)做為哈希表的關(guān)鍵字

s0=*(f+r)+s0;

NameList[i].k=s0;

}

}

/*-----------------------建立哈希表---------------------------------*/

void CreateHashList()

{

for (int i=0; iNAME_NO; i ++)

{

HashList[i].py="";

HashList[i].k=0;

HashList[i].si=0;

}

for (i=0; i NAME_NO ; i++)

{

int sum=0;

int adr=(NameList[i].k) % M; //哈希函數(shù)

int d=adr;

if(HashList[adr].si==0) //如果不沖突

{

HashList[adr].k=NameList[i].k;

HashList[adr].py=NameList[i].py;

HashList[adr].si=1;

}

else //沖突

{

do{

d=(d+((NameList[i].k))%10+1)%M; //偽散列

sum=sum+1; //查找次數(shù)加1

}while (HashList[d].k!=0);

HashList[d].k=NameList[i].k;

HashList[d].py=NameList[i].py;

HashList[d].si=sum+1;

}

}

}

/*-------------------------------------查找------------------------------------*/

void FindList()

{

printf("\n\n請(qǐng)輸入姓名的拼音: "); //輸入姓名

char name[20]={0};

scanf("%s",name);

int s0=0;

for (int r=0;r20;r++) //求出姓名的拼音所對(duì)應(yīng)的整數(shù)(關(guān)鍵字)

s0+=name[r];

int sum=1;

int adr=s0 % M; //使用哈希函數(shù)

int d=adr;

if(HashList[adr].k==s0) //分3種情況進(jìn)行判斷

printf("\n姓名:%s 關(guān)鍵字:%d 查找長(zhǎng)度為: 1",HashList[d].py,s0);

else if (HashList[adr].k==0)

printf("無(wú)該記錄!");

else

{

int g=0;

do

{

d=(d+s0%10+1)%M; //偽散列

sum=sum+1;

if (HashList[d].k==0)

{

printf("無(wú)記錄! ");

g=1;

}

if (HashList[d].k==s0)

{

printf("\n姓名:%s 關(guān)鍵字:%d 查找長(zhǎng)度為:%d",HashList[d].py,s0,sum);

g=1;

}

}while(g==0);

}

}

/*--------------------------------顯示哈希表----------------------------*/

void Display()

{

printf("\n\n地址\t關(guān)鍵字\t\t搜索長(zhǎng)度\tH(key)\t\t拼音 \n"); //顯示的格式

for(int i=0; i15; i++)

{

printf("%d ",i);

printf("\t%d ",HashList[i].k);

printf("\t\t%d ",HashList[i].si);

printf("\t\t%d ",(HashList[i].k)%M);

printf("\t %s ",HashList[i].py);

printf("\n");

}

printf("按任意鍵繼續(xù)顯示...\n"); //由于數(shù)據(jù)比較多,所以分屏顯示(以便在Win9x/DOS下能看到所有的數(shù)據(jù))

getchar();

for( i=15; i30; i++)

{

printf("%d ",i);

printf("\t%d ",HashList[i].k);

printf("\t\t%d ",HashList[i].si);

printf("\t\t%d ",(HashList[i].k)%M);

printf("\t %s ",HashList[i].py);

printf("\n");

}

printf("按任意鍵繼續(xù)顯示...\n");

getchar();

for( i=30; i40; i++)

{

printf("%d ",i);

printf("\t%d ",HashList[i].k);

printf("\t\t%d ",HashList[i].si);

printf("\t\t%d ",(HashList[i].k)%M);

printf("\t %s ",HashList[i].py);

printf("\n");

}

printf("按任意鍵繼續(xù)顯示...\n");

getchar();

for( i=40; i50; i++)

{

printf("%d ",i);

printf("\t%d ",HashList[i].k);

printf("\t\t%d ",HashList[i].si);

printf("\t\t%d ",(HashList[i].k)%M);

printf("\t %s ",HashList[i].py);

printf("\n");

}

float average=0;

for (i=0;i NAME_NO;i ++)

average+=HashList[i].si;

average/=NAME_NO;

printf("\n\n平均查找長(zhǎng)度:ASL(%d)=%f \n\n",NAME_NO,average);

}

/*--------------------------------主函數(shù)----------------------------*/

void main()

{

/* ::SetConsoleTitle("哈希表操作"); //Windows API函數(shù),設(shè)置控制臺(tái)窗口的標(biāo)題

HANDLE hCon = ::GetStdHandle(STD_OUTPUT_HANDLE); //獲得標(biāo)準(zhǔn)輸出設(shè)備的句柄

::SetConsoleTextAttribute(hCon, 10|0); //設(shè)置文本顏色

*/

printf("\n------------------------哈希表的建立和查找----------------------");

InitNameList();

CreateHashList ();

while(1)

{

printf("\n\n");

printf(" 1. 顯示哈希表\n");

printf(" 2. 查找\n");

printf(" 3. 退出\n");

err:

char ch1=getchar();

if (ch1='1')

Display();

else if (ch1='2')

FindList();

else if (ch1='3')

return;

else

{

printf("\n請(qǐng)輸入正確的選擇!");

goto err;

}

}

}

hashmap 中 hash 函數(shù)怎么是是實(shí)現(xiàn)的?還有哪些 hash 的實(shí)現(xiàn)方式

HashMap是對(duì)數(shù)據(jù)結(jié)構(gòu)中哈希表(Hash

Table)的實(shí)現(xiàn),Hash表又叫散列表。Hash表是根據(jù)關(guān)鍵碼Key來(lái)訪問(wèn)其對(duì)應(yīng)的值Value的數(shù)據(jù)結(jié)構(gòu),它通過(guò)一個(gè)映射函數(shù)把關(guān)鍵碼映射到表中一個(gè)位置來(lái)訪問(wèn)該位置的值,從而加快查找的速度。這個(gè)映射函數(shù)叫做Hash函數(shù),存放記錄的數(shù)組叫做Hash表。

在Java中,HashMap的內(nèi)部實(shí)現(xiàn)結(jié)合了鏈表和數(shù)組的優(yōu)勢(shì),鏈接節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)是Entry

,每個(gè)Entry對(duì)象的內(nèi)部又含有指向下一個(gè)Entry類型對(duì)象的引用,如以下代碼所示:

static

class

Entry

implements

Map.Entry

{

final

K

key;

V

value;

Entry

next;

//Entry類型內(nèi)部有一個(gè)自己類型的引用,指向下一個(gè)Entry

final

int

hash;

...

}

在HashMap的構(gòu)造函數(shù)中可以看到,Entry表被申明為了數(shù)組,如以下代碼所示:

public

HashMap()

{

this.loadFactor

=

DEFAULT_LOAD_FACTOR;

threshold

=

(int)(DEFAULT_INITIAL_CAPACITY

*

DEFAULT_LOAD_FACTOR);

table

=

new

Entry[DEFAULT_INITIAL_CAPACITY];

init();

}

在以上構(gòu)造函數(shù)中,默認(rèn)的DEFAULT_INITIAL_CAPACITY值為16,DEFAULT_LOAD_FACTOR的值為0.75。

當(dāng)put一個(gè)元素到HashMap中去時(shí),其內(nèi)部實(shí)現(xiàn)如下:

public

V

put(K

key,

V

value)

{

if

(key

==

null)

return

putForNullKey(value);

int

hash

=

hash(key.hashCode());

int

i

=

indexFor(hash,

table.length);

...

}


網(wǎng)頁(yè)標(biāo)題:哈希表鏈表Java代碼 鏈表 Java
文章URL:http://weahome.cn/article/hhjsoi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部