Java集合考點(diǎn)總結(jié)

一、ArrayList和Vector的區(qū)別

共同點(diǎn):

  • 都實(shí)現(xiàn)了List接口繼承了AbstractList接口
  • 底層實(shí)現(xiàn)都是數(shù)組。所以說(shuō)都是存儲(chǔ)有序的集合
  • 允許存儲(chǔ)null

不同點(diǎn):

  • ArrayList是線程不安全的,Vector則是線程安全的,即便我們需要線程安全的數(shù)組容器也是會(huì)選擇ArrayList,只是在初始化的時(shí)候通過(guò)Collections集合類的工具類來(lái)進(jìn)行包裝一下。因?yàn)檫@種同步實(shí)現(xiàn)更科學(xué)
  • 擴(kuò)容時(shí)ArrayList擴(kuò)容系數(shù)為1.5,Vector則是2

二、HashMap和Hashtable的區(qū)別

總的來(lái)說(shuō)HashTable的存在和Vector是一個(gè)意思。都是早期的實(shí)現(xiàn),并且盲目的保證了集合的線程安全。卻在一些不必要的操作上影響了集合的性能,并且早期函數(shù)的命名并不科學(xué)簡(jiǎn)潔嚴(yán)謹(jǐn)。

共同點(diǎn):

  • 實(shí)現(xiàn)了Map接口

不同點(diǎn):

  • HashMap非同步,HashTable同步。當(dāng)我們需要同步的映射容器的時(shí)候Java也提供了ConcurrentHashMap這個(gè)類。
  • HashMap允許null鍵和null值,HashTable不允許null鍵或者null值。
  • HashMap沒(méi)有保留HashTable的contains函數(shù),而且和HashTable一樣添加了containsValuecontainsKey函數(shù)。并選用containsKey函數(shù)來(lái)替代contains的功能。
  • 兩者繼承不同。HashTable繼承自Dictionary父類而HashMap繼承自AbstractMap父類。

三、List和Map的區(qū)別

存儲(chǔ)結(jié)構(gòu)不同

  • List是單列存儲(chǔ),Map是key-value鍵值存儲(chǔ)
  • List允許存重復(fù)元素,Map則key值不能重復(fù)
  • List存儲(chǔ)有序,Map映射存儲(chǔ)無(wú)序。

四、Set集合定義存儲(chǔ)在其中的元素是不能重復(fù)的,元素的重復(fù)與否是如何判斷的?是用==還是equals()?

首先需要清楚Set集合的常見(jiàn)實(shí)現(xiàn)類的底層實(shí)現(xiàn)用的都是Map映射的子類。

拿HashSet來(lái)舉例,它的底層是現(xiàn)金就是HashMap。而我們知道HashSet的存儲(chǔ)方式是把存入的元素對(duì)應(yīng)的存到了HashMap的key中,而value則是固定的Object對(duì)象的常量。

所以問(wèn)題就落到了HashMap是如何處理存入相同key的情況的呢?

HashMap源碼的632-635行是這樣判斷的,如果key值散列到的桶的頭元素與該key的hash值相等。并且key值相==或者key值equals的話,那么就認(rèn)為該key值已經(jīng)存在在了HashMap中。那么就跳出判斷循環(huán)。

Node<K,V> e; K k;
    if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
        
=============

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
        e.value = value;
    afterNodeAccess(e);
    return oldValue;
}

HashMap源碼652行做了如上處理,即拿出舊值并返回,對(duì)應(yīng)的節(jié)點(diǎn)的value替換上新值。

解答:

所以針對(duì)上述問(wèn)題,對(duì)于Set來(lái)說(shuō),當(dāng)插入重復(fù)元素的時(shí)候,就代表這HashMap要插入具有重復(fù)key值得節(jié)點(diǎn)。那么由于插入到Set集合的節(jié)點(diǎn)的value均是相同的無(wú)意義的Object常量對(duì)象。所以底層HashMap相當(dāng)于只是在頻繁的修改無(wú)疑的value。

從源碼可以看出來(lái)==equals兩者都用了。

五、Collection和Collections的區(qū)別

  • Collection是集合類的頂級(jí)接口,實(shí)現(xiàn)它的有Set接口和=,List接口,Queue接口。
  • Collections是集合的工具類。提供了一系列的靜態(tài)函數(shù)來(lái)對(duì)集合進(jìn)行搜索,查找,以及同步的一些操作等。

六、說(shuō)出ArrayList,LinkedList的存儲(chǔ)性能和特性

ArrayList底層是數(shù)組實(shí)現(xiàn),存儲(chǔ)有序。LinkedList底層是雙向鏈表實(shí)現(xiàn)的,存儲(chǔ)無(wú)序。

由于ArrayList底層實(shí)現(xiàn)是動(dòng)態(tài)數(shù)組,所以對(duì)于ArrayList來(lái)說(shuō)訪問(wèn)其元素可以根據(jù)角標(biāo)的方式訪問(wèn),非???。而LinkedList要訪問(wèn)元素的時(shí)候只能通過(guò)遍歷來(lái)找到要找的元素。所以ArrayList比LinkedList的訪問(wèn)速度要快,性能要好。

同樣的由于是數(shù)組的原因也就導(dǎo)致它在進(jìn)行增刪操作的時(shí)候是不方便的比較緩慢的(移動(dòng)數(shù)組元素,擴(kuò)容復(fù)制數(shù)組)。LinkedList只需要修改對(duì)應(yīng)節(jié)點(diǎn)next指針即可。消耗很小,所以LinkedList比ArrayList的增刪速度要快,性能要好。

注意:

但同時(shí)我們需要細(xì)想一個(gè)刪除操作具體體現(xiàn)的不僅僅是刪除這一個(gè)操作。對(duì)于不同的數(shù)據(jù)結(jié)構(gòu)而言,找到要?jiǎng)h除的元素也是一個(gè)性能差異很大的點(diǎn)。

比如對(duì)于LinkedList的刪除操作而言,刪除節(jié)點(diǎn)本身是很簡(jiǎn)易的,但是慢就慢在它需要對(duì)所有元素去迭代一遍來(lái)找這個(gè)元素。而對(duì)于ArrayList來(lái)說(shuō)刪除操作是不易的,因?yàn)樗媾R著被刪除位置后的數(shù)組元素前移的操作。而找元素這個(gè)操作對(duì)數(shù)組來(lái)說(shuō)卻是速度很快的一個(gè)操作。所以整體上來(lái)說(shuō)兩者增刪操作的性能差異就不是那么的確定了。比較時(shí)考慮的點(diǎn)還是比較的多

HashTable,SynchronizedMap,ConcurrentHashMap區(qū)別

HashTable

相比于HashMap,HashTable與其最大的區(qū)別就是所有函數(shù)都實(shí)現(xiàn)了同步。并且HashTable由于是最早設(shè)計(jì)的容器框架,有許多設(shè)計(jì)上的不足,并且后續(xù)并沒(méi)有改進(jìn)。最典型的元素插入時(shí)的put函數(shù),元素的散列位置是通過(guò)%來(lái)計(jì)算的,而非像HashMap中(table.length-1)&hash(key)

synchronizedMap

synchronizedMap可以作用于任何Map實(shí)現(xiàn)類上,但是相比于HashTable最大的區(qū)別則是它并不是盲目的對(duì)所有的函數(shù)都實(shí)現(xiàn)了同步。一定程度上性能要高于HashTable不少。但是兩者都是對(duì)象鎖。所以當(dāng)并發(fā)情況下有一個(gè)線程在執(zhí)行被鎖函數(shù)那么并發(fā)情況下的其它線程必然都是等待狀態(tài)。synchronizedMap相比于HashTable保證的只是非同步方法在多線程情況下的并行運(yùn)行。

潛在問(wèn)題:

Java代碼

// shm是SynchronizedMap的一個(gè)實(shí)例
if(shm.containsKey('key')){
        shm.remove(key);
}

這段代碼用于從map中刪除一個(gè)元素之前判斷是否存在這個(gè)元素。這里的 containsKey和reomve方法都是同步的,但是整段代碼卻不是??紤]這么一個(gè)使用場(chǎng)景:線程A執(zhí)行了containsKey方法返回 true,準(zhǔn)備執(zhí)行remove操作;這時(shí)另一個(gè)線程B開(kāi)始執(zhí)行,同樣執(zhí)行了containsKey方法返回true,并接著執(zhí)行了remove操作;然 后線程A接著執(zhí)行remove操作時(shí)發(fā)現(xiàn)此時(shí)已經(jīng)沒(méi)有這個(gè)元素了。要保證這段代碼按我們的意愿工作,一個(gè)辦法就是對(duì)這段代碼進(jìn)行同步控制,但是這么做付出 的代價(jià)太大。

同樣還有: 并發(fā)情況下的迭代及修改??赡芪覀?cè)诘臅r(shí)候某個(gè)線程對(duì)該Map正在執(zhí)行并發(fā)修改的操作。此時(shí)就會(huì)報(bào)出并發(fā)修改的異常

參考:SynchronizedMap和ConcurrentHashMap 區(qū)別

ConcurrentHashMap

ConcurrentHashMap相比于前兩者不但汲取了synchronizedMap的優(yōu)點(diǎn)并沒(méi)有對(duì)一些必要的函數(shù)實(shí)現(xiàn)同步。并且非同步的函數(shù)和HashMap的函數(shù)處理邏輯基本一致。而對(duì)于需要同步的函數(shù),其內(nèi)部采用的也是“桶鎖”,它不會(huì)對(duì)整個(gè)Map映射上鎖。這從一定程度上大大提升了性能,使其可以在多線程并發(fā)的情況下函數(shù)間并行執(zhí)行,而不會(huì)影響執(zhí)行結(jié)果和效率。

ArrayList和LinkedList增刪分析

ArrayList插入分析:

ArrayList的add(E e)函數(shù)默認(rèn)是尾插。 我們?nèi)绻捎迷摲椒ú迦氪罅吭氐脑?。不?huì)存在元素移位的情況。這種插入方式和LinkedList的插入方式一樣高效,復(fù)雜度都是O(1)。不存在元素移位的情況,而如果采用add(int index, E element)函數(shù)來(lái)對(duì)某個(gè)索引處的元素前插入大量元素的話,每插入一次元素都會(huì) 使數(shù)組索引后續(xù)元素都進(jìn)行一次元素移動(dòng)的操作,復(fù)雜度是O(n)。關(guān)鍵區(qū)別看你插入大量元素時(shí)想插到哪里

LinkedList插入分析:

LinkedList的add(E e)函數(shù)默認(rèn)插入方式是尾插法。由于它是雙向鏈表并且紀(jì)錄了頭尾兩個(gè)節(jié)點(diǎn)。所以用這個(gè)方式插入大量元素的時(shí)間復(fù)雜度是0(1)。addFirst和addLast同樣的道理由于紀(jì)錄了頭尾兩節(jié)點(diǎn)的原因。

ArrayList刪除分析:
而ArrayList提供的remove函數(shù)在刪除大量元素的時(shí)候,就必然會(huì)進(jìn)行數(shù)組元素的移位操作。它的耗時(shí)操作也就在此處。

LinkedList刪除分析:

LinkedList由于是雙向鏈表,所以頭刪尾刪都有性能上的優(yōu)勢(shì),復(fù)雜度均是O(1)。只需要考慮查找元素的復(fù)雜度即可。而且LinkedList中進(jìn)行了折半查找,先對(duì)元素的索引和總元素?cái)?shù)size進(jìn)行比較來(lái)確認(rèn)從頭迭代的找還是尾迭代的找。元素總規(guī)模先>>后才進(jìn)行的迭代。

擴(kuò)展:ArrayList和LinkedList增刪性能比較

ArrayList的增刪未必就是比LinkedList要慢。

如果增刪都是在末尾來(lái)操作【每次調(diào)用的都是remove()和add()】,此時(shí)ArrayList就不需要移動(dòng)數(shù)組元素了(增加元素的時(shí)候還是有可能觸發(fā)擴(kuò)容操作可能會(huì)擴(kuò)容并復(fù)制數(shù)組)。如果數(shù)據(jù)量有百萬(wàn)級(jí)的時(shí),ArrayList的速度是會(huì)比LinkedList要快的。(我測(cè)試過(guò))

如果刪除操作的位置是在中間。由于LinkedList的消耗主要是在遍歷上,ArrayList的消耗主要是在數(shù)組元素的移動(dòng)上(底層調(diào)用的是arraycopy()方法,是native方法)。
LinkedList的遍歷速度是要慢于ArrayList的數(shù)組元素移動(dòng)的速度的
如果數(shù)據(jù)量有百萬(wàn)級(jí)的時(shí),還是ArrayList要快。(我測(cè)試過(guò))

刪末尾元素 ArrayList 刪掉最后一個(gè)元素, LinkedList刪掉最后一個(gè)元素。指針的變化,相對(duì)復(fù)雜一些

刪中間元素,ArrayList需要做的是移動(dòng)數(shù)組后續(xù)元素。而arraycopy是native函數(shù),在JVM運(yùn)行時(shí)有性能優(yōu)勢(shì)
而且本身進(jìn)行的也是賦值操作,簡(jiǎn)單速度快

LinkedList需要做的是遍歷元素,內(nèi)存中指針的移動(dòng)相對(duì)速度慢于賦值操作一些。
所以LinkedList的遍歷速度要慢于ArrayList的移動(dòng)速度。

所以可以認(rèn)為百萬(wàn)級(jí)別的增刪,如果是在容器中間或中間以后的元素操作基本都是ArrayList快

七、Set集合的各實(shí)現(xiàn)類

首先有3個(gè),HashSet,TreeSet,LinkedHashSet

HashSet

常用的應(yīng)該是HashSet,底層實(shí)現(xiàn)是HashMap(由于LinkedHashSet繼承自HashSet,所以底層實(shí)現(xiàn)也提供了一種LinkedHashMap的構(gòu)造函數(shù)),無(wú)論存儲(chǔ)還是查詢都具有很好性能。都可以根據(jù)元素的hash值快速的散列到所在桶的位置,然后進(jìn)行相應(yīng)的操作。同時(shí)由于HashMap在jdk1.8的時(shí)候把HashMap底層的實(shí)現(xiàn)換成了散列表+紅黑樹(shù)的方式。導(dǎo)致即便碰巧大量元素散列到一個(gè)桶上也不會(huì)出現(xiàn)鏈表特別長(zhǎng),遍歷性能不好的情況。當(dāng)鏈表數(shù)量超過(guò)8個(gè)時(shí)會(huì)自動(dòng)將鏈表哦轉(zhuǎn)化成紅黑樹(shù)(元素?cái)?shù)大于64),遍歷時(shí)性能非常好。由于底層是HashMap實(shí)現(xiàn),所以我們?cè)诘臅r(shí)候調(diào)用iterator方法返回的其實(shí)是HashMap迭代時(shí)調(diào)用的keySet函數(shù)返回的Set集合的Iterator對(duì)象來(lái)進(jìn)行迭代。HashMap底層會(huì)對(duì)所有的Key用一個(gè)Set集合來(lái)進(jìn)行存儲(chǔ)(不重)。對(duì)所有value用一個(gè)Collection來(lái)進(jìn)行存儲(chǔ)。

TreeSet

TreeSet底層實(shí)現(xiàn)是TreeMap,即根據(jù)插入元素的自然排序或者自定義比較方式來(lái)比較插入元素的大小來(lái)決定其插入位置。進(jìn)而形成一個(gè)有序的紅黑樹(shù)。TreeSet和TreeMap一樣,插入的元素會(huì)有序的存儲(chǔ)在一個(gè)紅黑樹(shù)上。根據(jù)我們制定的規(guī)則,或者元素的自然排序順序。一般Tree系列的數(shù)據(jù)結(jié)構(gòu),我們用來(lái)保證一種有序性的數(shù)據(jù)結(jié)構(gòu)才會(huì)用,存入到其中的元素必須實(shí)現(xiàn)Comparable或者Comparator接口來(lái)保證元素的可比較性。我們一般都是重寫(xiě)compareTo或者compare方法來(lái)制定自己的比較規(guī)則,讓后續(xù)存到數(shù)據(jù)結(jié)構(gòu)中的元素滿足我們想要的順序。

LinkedHashSet

LinkedHashSet繼承自HashSet,底層實(shí)現(xiàn)是LinkedHashMap即散列表和雙向鏈表。HashSet也為L(zhǎng)inkedHashSet提供了一個(gè)構(gòu)造LinkedHashMap的構(gòu)造函數(shù),但需要注意的是,HashSet只為L(zhǎng)inkedHashSet提供了一種只有initialCapacityloadFactor的構(gòu)造函數(shù),也就意味著LinkedHashSet的元素?zé)o法像LinkedHashMap那樣具有兩種迭代順序,又由于默認(rèn)LinkedHashMap的迭代順序是元素的插入順序,所以它一般的作用就是用來(lái)保證元素的存儲(chǔ)順序和我們插入元素的順序一致。這樣我們?cè)诘@取元素的時(shí)候有一個(gè)預(yù)估值和期望值。

HashSet各方面性能好,不用排序一般用它,也是因?yàn)镠ashMap的優(yōu)勢(shì)。TreeSet一般用在比較根據(jù)元素進(jìn)行排序的時(shí)候,LinkedHashSet則是用到我們要元素的插入順序的時(shí)候。

八、Map集合及實(shí)現(xiàn)類

首先有3個(gè),HashMap,TreeMap,LinkedHashMap。

HashMap

最常用的一般是HashMap,底層實(shí)現(xiàn)是散列表+單鏈表+紅黑樹(shù)。從性能上來(lái)講,無(wú)論增刪還是查都可以根據(jù)傳入的key值快速定位到元素所在的桶。并且桶的數(shù)據(jù)結(jié)構(gòu)也對(duì)大量hash碰撞的特殊情況做了優(yōu)化,單鏈表元素?cái)?shù)超過(guò)8采用紅黑樹(shù)存儲(chǔ)。允許使用null做鍵值。迭代的時(shí)候有三種方式

1.
for (Entry<String, String> entry : map.entrySet())

2.
for (String keys : map.keySet())

3.
Iterator<String> keys = map.keySet().iterator();
while (keys.hasNext()){
    
}
  • 第一種調(diào)用的是HashIterator。根據(jù)散列表的數(shù)組下標(biāo)來(lái)進(jìn)行迭代。
  • 第二種和第三種其實(shí)是一樣的。keySet()返回的KeySet類繼承自AbstractSet,在foreach迭代的時(shí)候會(huì)自動(dòng)調(diào)用該類的iterator()方法,返回KeyIterator類,而這個(gè)類又繼承自HashIterator類。只是實(shí)現(xiàn)了next()方法,但函數(shù)內(nèi)也還是調(diào)用了HashIterator類的nextNode()方法。
final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

TreeMap

TreeMap是TreeSet的底層實(shí)現(xiàn)。底層數(shù)據(jù)結(jié)構(gòu)式紅黑樹(shù),實(shí)現(xiàn)了SortedMap接口,該映射是根據(jù)其鍵的自然順序進(jìn)行排序的,或者根據(jù)在構(gòu)建TreeMap時(shí)傳入的Comparator來(lái)進(jìn)行排序。需要注意,存入的元素必須實(shí)現(xiàn)了Comparable接口或者其類型被指定的Comparator所接受。而且比較的對(duì)象參數(shù)不能為null。在使用這種數(shù)據(jù)結(jié)構(gòu)的時(shí)候我們要注的除了是元素需要具有可比性,同時(shí)我們還需要注意元素具體的比較規(guī)則,即元素的類重寫(xiě)的compare方法和compareTo方法的比較邏輯。

LinkedHashMap

LinkedHashMap是LinkedHashSet的底層實(shí)現(xiàn),繼承自HashMap,底層數(shù)據(jù)結(jié)構(gòu)是散列表+單鏈表+紅黑樹(shù)+雙向鏈表。所以和HashMap一樣無(wú)論增刪還是查找元素都具有極高的性能。不同的是迭代元素的方式。HashMap是通過(guò)迭代每一個(gè)“桶”來(lái)進(jìn)行的迭代,迭代時(shí)獲取的Entry都是未知的。而LinkedHashMap則默認(rèn)是通過(guò)節(jié)點(diǎn)的插入順序來(lái)進(jìn)行迭代,或者通過(guò)節(jié)點(diǎn)的最近最少來(lái)進(jìn)行迭代。之所以可以如此,是因?yàn)長(zhǎng)inkedHashMap擴(kuò)展了HashMap的Node節(jié)點(diǎn)。LinkedHashMap的Entry節(jié)點(diǎn)比其多維護(hù)兩個(gè)指針變量。分別指向該節(jié)點(diǎn)的前驅(qū)和后繼節(jié)點(diǎn),所以比HashMap可以多維護(hù)一個(gè)雙向鏈表。

/**
 * HashMap.Node subclass for normal LinkedHashMap entries.
 */
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

LinkedHashMap默認(rèn)是按key的插入順序來(lái)排序,也可以通過(guò)在構(gòu)造時(shí)通過(guò)指定accessOrder為true來(lái)使其按元素的LRU順序來(lái)排序,在其迭代的時(shí)候可以看出它的元素排列順序。

LinkedHashMap不允許存儲(chǔ)的元素的key值為null。兩種比較方式都不支持。元素在插入到紅黑樹(shù)的時(shí)候肯定會(huì)與其他樹(shù)中的節(jié)點(diǎn)進(jìn)行比較,此時(shí)會(huì)報(bào)錯(cuò)。

參考:HashMap,LinkedHashMap,TreeMap的區(qū)別

九、Enumeration和Iterator接口的區(qū)別

Enumeration最早出來(lái)主要是針對(duì)當(dāng)時(shí)所有的集合類的如Vector和HashTable的。但是由于原本Vector與Hashtable設(shè)計(jì)上的問(wèn)題,以及Enumeration在設(shè)計(jì)上的一些缺陷到之后來(lái)出現(xiàn)了Iterator替代了它。

并且Iterator支持所有集合類。并對(duì)Enumeration進(jìn)行了擴(kuò)展,1. 添加了一個(gè)可選擇的刪除操作。并且在2. 函數(shù)命名上也更加科學(xué),簡(jiǎn)潔了。同時(shí)Iterator也在并發(fā)操作的時(shí)候針對(duì)其實(shí)現(xiàn)類在并發(fā)情況下可能出現(xiàn)的3.并發(fā)修改問(wèn)題利用fail-fast機(jī)制進(jìn)行補(bǔ)足。Enumeration由于本身針對(duì)的就是并發(fā)情況下同步的類Vector和HashTable所以不存在并發(fā)上的問(wèn)題。

十、ListIterator的特點(diǎn)

我們前面說(shuō)到Enumeration最早吹來(lái)是服務(wù)于Vector和HashTable來(lái)進(jìn)行遍歷的。后來(lái)Iterator的出現(xiàn)替代了它,但是對(duì)應(yīng)不同的集合類迭代方式也不盡相同。所以ListIterator繼承自Iterator,看類名就知道他是負(fù)責(zé)List集合的迭代的。

  • 繼承自Iterator接口,用于遍歷List集合的元素。
  • 可以實(shí)現(xiàn)雙向遍歷。添加元素。設(shè)置元素

十一、Java中HashMap的key值要是為類對(duì)象則該類需要滿足什么條件?

需要同時(shí)重寫(xiě)該類的hashCode()方法和它的equals()方法。

因?yàn)镠ashMap在插入元素的時(shí)候在put函數(shù)中就會(huì)向調(diào)用的putVal函數(shù)傳遞key的hash值。然后putVal函數(shù)再對(duì)key的哈希值進(jìn)行散列,找到對(duì)應(yīng)的桶。既然這樣,那和重寫(xiě)對(duì)象到的hashcode有什么關(guān)系呢?

那是因?yàn)槲覀兊膶?duì)象做為key來(lái)存到hashMap中的話,我們最后肯定是根據(jù)key來(lái)獲取到對(duì)應(yīng)的value。而這個(gè)entry又存儲(chǔ)在桶中,我們要想找到對(duì)應(yīng)的桶,對(duì)于HashMap函數(shù)來(lái)說(shuō)只能通過(guò)對(duì)象的hashcode值來(lái)進(jìn)行散列,散列到對(duì)應(yīng)的桶上。但是我們存儲(chǔ)和取出的時(shí)候其實(shí)用的是兩個(gè)對(duì)象(兩次都會(huì)new新的對(duì)象)。但是兩個(gè)對(duì)象具有相同的屬性值。

如果我們不重寫(xiě)對(duì)象的hashcode的函數(shù)的話,去更改他hash值的生成策略的話是無(wú)法散列到對(duì)應(yīng)的桶的(根據(jù)對(duì)象地址生成的hash值)。也就無(wú)法取出我們存進(jìn)去的值。所以我們需要重寫(xiě)它的hashcode函數(shù)讓它生成是只需要根據(jù)對(duì)象的屬性進(jìn)行生成即可,這樣下次我們無(wú)論穿進(jìn)去這個(gè)類的哪個(gè)對(duì)象,只要它保證我們指定的生成hashcode參考的屬性相等即可。equals函數(shù)就更不用說(shuō)了,Object類的equals默認(rèn)比較的是兩個(gè)對(duì)象的地址。而對(duì)我們開(kāi)發(fā)來(lái)說(shuō)這樣的意義并不大。一般來(lái)說(shuō)我們認(rèn)為只要兩個(gè)對(duì)象的成員變量的值是相等的,那么我們就認(rèn)為這兩個(gè)對(duì)象是相等的!

顯然hashcode相比于equals函數(shù)具偶然性。在hashmap元素的比較中也是把hash值得比較前置了。因?yàn)閔ashcode可能在一些情況下出現(xiàn)hash碰撞相等的情況。所以我們重寫(xiě)equals函數(shù)前必須先重寫(xiě)hashcode函數(shù),這樣既保證了正確性,也在性能上得到了提升。

十二、與Java集合框架相關(guān)的有哪些最好的實(shí)踐

  1. 根據(jù)需要確定集合的類型。如果是單列的集合,我們考慮用Collection下的子接口ArrayList和Set。如果是映射,我們就考慮使用Map
  2. 確定完我們的集合類型,我們接下來(lái)確定使用該集合類型下的哪個(gè)子類~我認(rèn)為可以簡(jiǎn)單分成幾個(gè)步驟:
  • 是否需要同步

    • 找線程安全的集合類使用
  • 迭代時(shí)是否需要有序(插入順序有序)

    • 找Linked雙向列表結(jié)構(gòu)的
  • 是否需要排序(自然順序或者手動(dòng)排序)

    • 找Tree紅黑樹(shù)類型的(JDK1.8)
  1. 估算存放集合的數(shù)據(jù)量有多大,無(wú)論是List還是Map,它們實(shí)現(xiàn)動(dòng)態(tài)增長(zhǎng),都是有性能消耗的。在初始集合的時(shí)候給出一個(gè)合理的容量會(huì)減少動(dòng)態(tài)增長(zhǎng)時(shí)的消耗
  2. 使用泛型,避免在運(yùn)行時(shí)出現(xiàn)ClassCastException
  3. 盡可能使用Collections工具類,或者獲取只讀、同步或空的集合,而非編寫(xiě)自己的工具函數(shù)。它提供了我們常用的針對(duì)List集合的功能函數(shù),具有很高的重用性,以及更好的穩(wěn)定性和可維護(hù)性。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java集合類可用于存儲(chǔ)數(shù)量不等的對(duì)象,并可以實(shí)現(xiàn)常用的數(shù)據(jù)結(jié)構(gòu)如棧,隊(duì)列等,Java集合還可以用于保存具有映射關(guān)...
    小徐andorid閱讀 2,091評(píng)論 0 13
  • 一、集合入門(mén)總結(jié) 集合框架: Java中的集合框架大類可分為Collection和Map;兩者的區(qū)別: 1、Col...
    程序員歐陽(yáng)閱讀 11,815評(píng)論 2 61
  • 集合類框架的介紹: ![Java 集合類框架](https://upload-images.jianshu.io/...
    LynnGuo閱讀 804評(píng)論 0 1
  • 在一個(gè)方法內(nèi)部定義的變量都存儲(chǔ)在棧中,當(dāng)這個(gè)函數(shù)運(yùn)行結(jié)束后,其對(duì)應(yīng)的棧就會(huì)被回收,此時(shí),在其方法體中定義的變量將不...
    Y了個(gè)J閱讀 4,576評(píng)論 1 14
  • 冬陽(yáng),把每一處被秋風(fēng)吹得拔涼的角落,一個(gè)個(gè)的煨暖。從晨曦到日中,它的光占據(jù)了每一處可以觸及到的地方。大伙兒...
    孔子顏淵閱讀 688評(píng)論 12 5

友情鏈接更多精彩內(nèi)容