常見java集合的實現細節

一、set 和map 

set 代表一種集合元素無序、集合元素不可重複的集合,map則代表一種由多個key-value對組成的集合,map集合類似於傳統的關聯數組。表面上看它們之間相似性很少,但實際上兩者之間有莫大的關聯,可以說,map是set的擴展。


1.1 set 和map的關係


對比兩者的繼承類體系,可以發現有相似的地方



這些Map集合的key具有一個特徵:所有key不能重複,key之間沒有順序。也就是說,如果將Map集合的所有key集中起來,那這些key就組成了一個set集合。所以,發現map集合提供瞭如下方法來返回所有key組成的set集合。

set<k>  KeySet ()

由此可見,map集合的所有key將具有set集合的特徵,只要把map的所有key集中起來看,那它就是一個set,實現了從map到set的轉換。


換一種思維來理解map集合,如果把map集合中的value當成key的“附屬物”,那麼map集合在保存key-value對時只考慮key即可。


對於一個map集合而言,它本質上是一個關聯數組。

下面爲map集合的示意圖


一個是key數組,一個是value數組,如果將key和與之對應的value綁定,就可以使用一個set集合來保存。


1.2  HashMap 和 HashSet


實際上,這兩者之間有很多相似之處。對於hashset,系統採用hash算法決定集合元素的存儲位置,這樣可以保證快速存,取集合元素;對於HashMap而言,系統將value當成key的附屬,系統根據hash算法來決定key的存儲位置,這樣可以保證快速存,取key,而value總是緊隨key存儲。


雖然集合號稱存儲的是java對象,但實際上並不會真正將java對象放入set集合中,而只是在set集合中保留這些對象的引用而已。也就是,java集合實際上是多個引用變量所組成的集合,這些引用變量指向實際的java對象。


對於每個java集合來說,其實只是多個引用變量的集合。下面程序證明


import java.util.List;
import java.util.ArrayList;




class apple
{
double weight;
public apple(double weight)
{
this.weight=weight;
}

}
public class inittest {
public static void main(String[] args)
{
apple t1=new apple(1.3);
apple t2=new apple(2.3);
List<apple> list=new ArrayList<apple>(4);
list.add(t1);
list.add(t2);
System.out.println(list.get(0)==t1);
System.out.println(list.get(1)==t2);

}
}

程序輸出結果爲 

true
true

程序執行時,內存分配情況如下,


從中可看出,arraylist中存儲的是對象的引用,而不是對象本身,此時長度我們給定4個長度,如果不給,系統默認爲10,


對於hashmap而言,它的存儲方式較arraylist複雜些,採用一種hash算法,來決定每個元素的存儲位置。

HashMap<String, double> map= new HashMap<String,double>();

map.put("語文“,80.0);

....

當程序執行map.put時,系統調用”語文“的hashcode()方法得到其hashcode值,每個java對象都有hashcode()方法,都可以得到hashcode值,然後系統根據這個值來決定該元素的存儲位置。


當向hashmap中添加key-value對,由其key的hashcode()返回值決定該key-value對(即entry)的存儲位置。當兩個entry對象的key的hashcode值相等時,將由key通過eqauls()比較值決定是採用覆蓋行爲,還是產生entry鏈。


當創建一個hashmap時,系統會創建一個table數組來保存hashmap中的entry,數組大小爲找出大於initialcapacity,最小的2的n次方,hashmap包含如下幾個構造器

hashmap();

hashmap(int initialcapacity);

hashmap(int initialcapacity,float loadfactor);

所以,創建hashmap時指定的容量並不是等於實際的容量。


上面是一個table數組,每一個格叫bucket,用來存儲元素(entry)的位置,hashmap之所以能快速存,取的原因是因爲:不同的東西放在不同的地方,需要時才能快速找到!



hashset的絕大部分方法都是通過調用hashmap的方法來實現的,因此hashset,hashset 兩個集合本質上是相同的。

import java.util.Set;
import java.util.HashSet;




class name
{
private String first;
private String last;
public name(String first,String last)
{
this.first=first;
this.last=last;
}
public boolean equals(Object o)
{
if(this==o)
{
return true;
}
if(o.getClass()==name.class)
{
name n=(name)o;
return n.first.equals(first)&&n.last.equals(last);
}
return false;
}

}
public class inittest {
public static void main(String[] args)
{
Set<name> s=new HashSet<name>();
s.add(new name("abc","123"));
System.out.println(s.contains(new name("abc","123")));

}
}

程序結果爲false

上面程序向hashset添加一個對象之後,立即通過程序判斷該hashset中是否有這個對象,結果是沒有。這是因爲hashset判斷兩個對象是否相等的標準除了要求通過equals返回true之外,還要求兩個hashcode返回值相等。上面程序沒有重寫hashcode方法。所以,當試圖把某個類的對象當成hashmap的key,或者試圖將這個類的對象放入hashset中保存時,重寫該類的equals()和hashcode()很重要,返回值必須相等。


1.2 treeset 和treemap 


treeset 底層實際使用的存儲容器就是treemap,對於treemap而言,採用的是紅黑樹的排序二叉樹來保存map中的每一個entry,每一個entry被當成一個節點對待。

 

1.3 map 和list的關係


map接口提供get(K key)方法允許map對象根據key來獲取value;

list接口提供了get( int  index)方法允許list對象根據元素索引來獲取value;


java要求各種集合都提供一個iterator()方法,該方法可以返回一個iterator用於遍歷該集合中元素,至於返回的iterator到底是哪兒種實現類,程序並不關心,這就是典型的”迭代器模式“



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章