1、數組和集合的比較java
數組不是面向對象的,存在明顯的缺陷,集合彌補了數組的缺點,比數組更靈活更實用,並且不一樣的集合框架類可適用不一樣場合。以下:
1:數組能存放基本數據類型和對象,而集合類存放的都是對象的引用,而非對象自己!
2:數組容易固定沒法動態改變,集合類容量動態改變。
3:數組沒法判斷其中實際存有多少元素,length只告訴了數組的容量,而集合的size()能夠確切知道元素的個數
4:集合有多種實現方式和不一樣適用場合,不像數組僅採用順序表方式
5:集合以類的形式存在,具備封裝、繼承、多態等類的特性,經過簡單的方法和屬性便可實現各類複雜操做,大大提升了軟件的開發效率面試
2、Java集合算法
此圖可用Windows系統自帶畫圖工具查看比較清晰數組
Collection和Map,是集合框架的根接口。安全
Collection的子接口:服務器
Set:接口 ---實現類: HashSet、LinkedHashSet
Set的子接口SortedSet接口---實現類:TreeSet
List:接口---實現類: LinkedList,Vector,ArrayList 數據結構
List集合併發
有序列表,容許存放重複的元素;
實現類:
ArrayList:數組實現,查詢快,增刪慢,輕量級;(線程不安全)
LinkedList:雙向鏈表實現,增刪快,查詢慢 (線程不安全)
Vector:數組實現,重量級 (線程安全、使用少)app
ArrayList框架
底層是Object數組,因此ArrayList具備數組的查詢速度快的優勢以及增刪速度慢的缺點。
而在LinkedList的底層是一種雙向循環鏈表。在此鏈表上每個數據節點都由三部分組成:前指針(指向前面的節點的位置),數據,後指針(指向後面的節點的位置)。最後一個節點的後指針指向第一個節點的前指針,造成一個循環。
雙向循環鏈表的查詢效率低可是增刪效率高。
ArrayList和LinkedList在用法上沒有區別,可是在功能上仍是有區別的。
LinkedList
LinkedList是採用雙向循環鏈表實現的。
利用LinkedList實現棧(stack)、隊列(queue)、雙向隊列(double-ended queue )。
它具備方法addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等。
常常用在增刪操做較多而查詢操做不多的狀況下:
隊列和堆棧。
隊列:先進先出的數據結構。
棧:後進先出的數據結構。
注意:使用棧的時候必定不能提供方法讓不是最後一個元素的元素得到出棧的機會。
Vector
(與ArrayList類似,區別是Vector是重量級的組件,使用使消耗的資源比較多。)
結論:在考慮併發的狀況下用Vector(保證線程的安全)。
在不考慮併發的狀況下用ArrayList(不能保證線程的安全)。
ArrayList自動擴充機制
實現機制:ArrayList.ensureCapacity(int minCapacity)
首先獲得當前elementData 屬性的長度oldCapacity。
而後經過判斷oldCapacity和minCapacity參數誰大來決定是否須要擴容, 若是minCapacity大於
oldCapacity,那麼咱們就對當前的List對象進行擴容。
擴容的的策略爲:取(oldCapacity * 3)/2 + 1和minCapacity之間更大的那個。而後使用數組拷
貝的方法,把之前存放的數據轉移到新的數組對象中
若是minCapacity不大於oldCapacity那麼就不進行擴容。
用LinkedList實現隊列:
隊列(Queue)是限定全部的插入只能在表的一端進行,而全部的刪除都在表的另外一端進行的線性表。
表中容許插入的一端稱爲隊尾(Rear),容許刪除的一端稱爲隊頭(Front)。
隊列的操做是按先進先出(FIFO)的原則進行的。
隊列的物理存儲能夠用順序存儲結構,也能夠用鏈式存儲結構。
用LinkedList實現棧:
棧(Stack)也是一種特殊的線性表,是一種後進先出(LIFO)的結構。
棧是限定僅在表尾進行插入和刪除運算的線性表,表尾稱爲棧頂(top),表頭稱爲棧底(bottom)。
棧的物理存儲能夠用順序存儲結構,也能夠用鏈式存儲結構。
List經常使用方法:
void add(int index, Object element) :添加對象element到位置index上
boolean addAll(int index, Collection collection) :在index位置後添加容器collection中全部的元素
Object get(int index) :取出下標爲index的位置的元素
int indexOf(Object element) :查找對象element 在List中第一次出現的位置
int lastIndexOf(Object element) :查找對象element 在List中最後出現的位置
Object remove(int index) :刪除index位置上的元素
ListIterator listIterator(int startIndex) :返回一個ListIterator 跌代器,開始位置爲startIndex
List subList(int fromIndex, int toIndex) :返回一個子列表List ,元素存放爲從 fromIndex 到toIndex以前的一個元素
Set集合
擴展Collection接口
無序集合,不容許存放重複的元素;容許使用null元素
對 add()、equals() 和 hashCode() 方法添加了限制
HashSet和TreeSet是Set的實現
Set—》hashSet linkedHashSet
SortedSet —》 TreeSet
HashSet 的後臺有一個HashMap;初始化後臺容量;只不過生成一個HashSet的話,系統只提供key的訪問; 若是有兩個Key重複,那麼會覆蓋以前的;
實現類 :
HashSet:equals返回true,hashCode返回相同的整數;哈希表;存儲的數據是無序的。
LinkedHashSet:此實現與 HashSet 的不一樣以外在於,後者維護着一個運行於全部條目的雙重連接列表。存儲的數據是有序的。
HashSet類直接實現了Set接口, 其底層實際上是包裝了一個HashMap去實現的。HashSet採用HashCode算法來存取集合中的元素,所以具備比較好的讀取和查找性能。
不只不能保證元素插入的順序,並且在元素在之後的順序中也可能變化(這是由HashSet按HashCode存儲對象(元素)決定的,對象變化則可能致使HashCode變化)
HashSet是線程非安全的
HashSet元素值能夠爲NULL
HashSet經常使用方法:
public boolean contains(Object o) :若是set包含指定元素,返回true
public Iterator iterator()返回set中元素的迭代器
public Object[] toArray() :返回包含set中全部元素的數組public Object[] toArray(Object[] a) :返回包含set中全部元素的數組,返回數組的運行時類型是指定數組的運行時類型
public boolean add(Object o) :若是set中不存在指定元素,則向set加入
public boolean remove(Object o) :若是set中存在指定元素,則從set中刪除
public boolean removeAll(Collection c) :若是set包含指定集合,則從set中刪除指定集合的全部元素
public boolean containsAll(Collection c) :若是set包含指定集合的全部元素,返回true。若是指定集合也是一個set,只有是當前set的子集時,方法返回true
實現Set接口的HashSet,依靠HashMap來實現的。
咱們應該爲要存放到散列表的各個對象定義hashCode()和equals()。
前面說過,Set集合是不容許重複元素的,不然將會引起各類奇怪的問題。那麼HashSet如何判斷元素重複呢?
HashSet須要同時經過equals和HashCode來判斷兩個元素是否相等,具體規則是,若是兩個元素經過equals爲true,而且兩個元素的hashCode相等,則這兩個元素相等(即重複)。
因此若是要重寫保存在HashSet中的對象的equals方法,也要重寫hashCode方法,重寫先後hashCode返回的結果相等(即保證保存在同一個位置)。全部參與計算 hashCode() 返回值的關鍵屬性,都應該用於做爲 equals() 比較的標準。
試想若是重寫了equals方法但不重寫hashCode方法,即相同equals結果的兩個對象將會被HashSet看成兩個元素保存起來,這與咱們設計HashSet的初衷不符(元素不重複)。
另外若是兩個元素哈市Code相等但equals結果不爲true,HashSet會將這兩個元素保存在同一個位置,並將超過一個的元素以鏈表方式保存,這將影響HashSet的效率。
若是重寫了equals方法但沒有重寫hashCode方法,則HashSet可能沒法正常工做,好比下面的例子。
上面註釋了hashCode方法,因此你將會看到下面的結果。
false
[R[count:9 # hashCode:14927396], R[count:5 # hashCode:24417480], R[count:-2 # hashCode:31817359], R[count:-3 # hashCode:13884241]]
取消註釋,則結果就正確了 copy
true
[R[count:5 # hashCode:5], R[count:9 # hashCode:9], R[count:-3 # hashCode:-3], R[count:-2 # hashCode:-2]]
如何達到不能存在重複元素的目的?
「鍵」就是咱們要存入的對象,「值」則是一個常量。這樣能夠確保,咱們所須要的存儲的信息
之是「鍵」。而「鍵」在Map中是不能重複的,這就保證了咱們存入Set中的全部的元素都不重複。
HashSet如何過濾重複元素
調用元素HashCode得到哈希碼--》判斷哈希碼是否相等,不相等則錄入
---》相等則判斷equals()後是否相等,不相等在進行 hashcode錄入,相等不錄入
LinkedHashSet的特徵
LinkedHashSet是HashSet的一個子類,LinkedHashSet也根據HashCode的值來決定元素的存儲位置,但同時它還用一個鏈表來維護元素的插入順序,插入的時候即要計算hashCode又要維護鏈表,而遍歷的時候只須要按鏈表來訪問元素。查看LinkedHashSet的源碼發現它是樣的,
在JAVA7中, LinkedHashSet沒有定義任何方法,只有四個構造函數,它的構造函數調用了父類(HashSet)的帶三個參數的構造方法,父類的構造函數以下,
由此可知,LinkedHashSet本質上也是從LinkedHashMap而來,LinkedHashSet的全部方法都繼承自HashSet, 而它能維持元素的插入順序的性質則繼承自LinkedHashMap.
下面是一個LinkedHashSet維持元素插入順序的例子,
輸入以下
TreeSet實現了SortedSet接口,顧名思義這是一種排序的Set集合,查看jdk源碼發現底層是用TreeMap實現的,本質上是一個紅黑樹原理。 正由於它是排序了的,因此相對HashSet來講,TreeSet提供了一些額外的按排序位置訪問元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().
TreeSet的排序分兩種類型,一種是天然排序,另外一種是定製排序。
TreeSet 會調用compareTo方法比較元素大小,而後按升序排序。因此天然排序中的元素對象,都必須實現了Comparable接口,不然會跑出異常。對於TreeSet判斷元素是否重複的標準,也是調用元素從Comparable接口繼承而來額compareTo方法,若是返回0則是重複元素(兩個元素I相等)。Java的常見類都已經實現了Comparable接口,下面舉例說明沒有實現Comparable存入TreeSet時引起異常的狀況。
運行程序會拋出以下異常
將上面的Err類實現Comparable接口以後程序就能正常運行了
還有個重要問題是,由於TreeSet會調用元素的compareTo方法,這就要求全部元素的類型都相同,不然也會發生異常。也就是說,TreeSet只容許存入同一類的元素。例以下面這個例子就會拋出類型轉換異常
運行結果
TreeSet還有一種排序就是定製排序,定製排序時候,須要關聯一個 Comparator對象,由Comparator提供排序邏輯。下面就是一個使用Lambda表達式代替Comparator對象來提供定製排序的例子。 下面是一個定製排序的列子
固然將Comparator直接寫入TreeSet初始化中也能夠。以下。
TreeSet是依靠TreeMap來實現的。
TreeSet是一個有序集合,TreeSet中元素將按照升序排列,缺省是按照天然順序進行排列,意味着TreeSet中元素要實現Comparable接口
咱們能夠在構造TreeSet對象時,傳遞實現了Comparator接口的比較器對象。
Comparable和Comparator
Comparable 接口以提供天然排序順序。
對於那些沒有天然順序的類、或者當您想要一個不一樣於天然順序的順序時,您能夠實現
Comparator 接口來定義您本身的排序函數。能夠將Comparator傳遞給Collections.sort或Arrays.sort。
Comparator接口
當一個類並未實現Comparable,或者不喜歡缺省的Comaparable行爲。能夠實現Comparator接口
直接實現Comparator的compare接口完成自定義比較類。
例:Arrays.sort(results, new Comparator<RepDataQueryResultVO>() 數組排序 RepDataQueryExecutor
例:Collections.sort(lst,new Comparator<TaskPrintSchemeVO>()
EnumSet顧名思義就是專爲枚舉類型設計的集合,所以集合元素必須是枚舉類型,不然會拋出異常。 EnumSet集合也是有序的,其順序就是Enum類內元素定義的順序。EnumSet存取的速度很是快,批量操做的速度也很快。EnumSet主要提供如下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet並無提供任何構造函數,要建立一個EnumSet集合對象,只須要調用allOf等方法,下面是一個EnumSet的例子。
執行結果
幾種Set的比較:
HashSet外部無序地遍歷成員。
成員可爲任意Object子類的對象,但若是覆蓋了equals方法,同
時注意修改hashCode方法。
TreeSet外部有序地遍歷成員;
附加實現了SortedSet, 支持子集等要求順序的操做
成員要求實現Comparable接口,或者使用Comparator構造
TreeSet。成員通常爲同一類型。
LinkedHashSet外部按成員的插入順序遍歷成員
成員與HashSet成員相似
HashSet是基於Hash算法實現的,其性能一般都優於TreeSet。咱們一般都應該使用HashSet,在咱們須要排序的功能時,咱們才使用TreeSet。
HashSet的元素存放順序和咱們添加進去時候的順序沒有任何關係,而LinkedHashSet 則保持元素的添加順序。TreeSet則是對咱們的Set中的元素進行排序存放。
通常來講,當您要從集合中以有序的方式抽取元素時,TreeSet 實現就會有用處。爲了能順利進行,添加到 TreeSet 的元素必須是可排序的。 而您一樣須要對添加到TreeSet中的類對象實現 Comparable 接口的支持。通常說來,先把元素添加到 HashSet,再把集合轉換爲 TreeSet 來進行有序遍歷會更快。
HashSet和TreeSet是Set集合中用得最多的I集合。HashSet老是比TreeSet集合性能好,由於HashSet不須要額維護元素的順序。
LinkedHashSet須要用額外的鏈表維護元素的插入順序,所以在插入時性能比HashSet低,但在迭代訪問(遍歷)時性能更高。由於插入的時候即要計算hashCode又要維護鏈表,而遍歷的時候只須要按鏈表來訪問元素。
EnumSet元素是全部Set元素中性能最好的,可是它只能保存Enum類型的元素
Map
集合框架的第二類接口樹。
它提供了一組鍵值的映射。其中存儲的每一個對象都有一個相應的關鍵字(key),關鍵字決定了對象在Map中的存儲位置。
關鍵字應該是惟一的,每一個key 只能映射一個value。
實現類:
HashMap、TreeMap、LinkedHashMap、Hashtable等
HashMap:鍵值對,key不能重複,可是value能夠重複;key的實現就是HashSet;value對應着放;容許null的鍵或值;
Hashtable:線程安全的,不容許null的鍵或值;
Properties::key和value都是String類型,用來讀配置文件;
TreeMap:對key排好序的Map; key 就是TreeSet, value對應每一個key; key要實現Comparable接口或TreeMap有本身的構造器;
LinkedHashMap: 此實現與 HashMap 的不一樣之處在於,後者維護着一個運行於全部條目的雙重連接列表。存儲的數
據是有序的。
HashMap:
Map 主要用於存儲鍵(key)值(value)對,根據鍵獲得值,所以鍵不容許重複,但容許值重複。
HashMap 是一個最經常使用的Map,它根據鍵的HashCode 值存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度。
HashMap最多隻容許一條記錄的鍵爲Null;容許多條記錄的值爲 Null;
HashMap不支持線程的同步,即任一時刻能夠有多個線程同時寫HashMap;可能會致使數據的不一致。若是須要同步,能夠用 Collections的synchronizedMap方法使HashMap具備同步的能力。
HashMap實現原理---散列
Hash哈希算法的意義在於提供了一種快速存取數據的方法,它用一種算法創建鍵值與真實值之間的對應關係。散列表又稱爲哈希表。散列表算法的基本思想是:以結點的關鍵字爲自變量,經過必定的函數關係(散列函數)計算出對應的函數值,以這個值做爲該結點存儲在散列表中地址。
當散列表中的元素存放太滿,就必須進行再散列,將產生一個新的散列表,全部元素存放到新的散列表中,原先的散列表將被刪除。在Java語言中,經過負載因子(load factor)來決定什麼時候對散列表進行再散列。例如:若是負載因子0.75,當散列表中已經有75%位置已經放滿,那麼將進行再散列。
負載因子越高(越接近1.0),內存的使用效率越高,元素的尋找時間越長。負載因子越低(越接近0.0),元素的尋找時間越短,內存浪費越多。
什麼時候需重寫equals?
當一個類有本身特有的「邏輯相等」概念(不一樣於對象身份的概念);
Object類僅僅提供了一個對引用的比較,若是兩個引用不是同一個那就返回false,這是沒法知足大多數對象比較的須要的,因此要覆蓋;
使用==操做符檢查實參是否爲指向對象的引用」
使用instanceof操做符檢查實參是否爲正確的類型
把實參轉換到正確的類型;
對於該類中每個「關鍵」域,檢查實參中的域與當前對象中對應的域值是否匹 配。對於既不是float也不是double類型的基本類型的域,可使用==操做符 進行比較;對於對象引用類型的域,能夠遞歸地調用所引用的對象的equals方法,對於float和double類型的域,先轉換成int或long類型的值,而後使用==操做符比較;
當你編寫完成了equals方法以後,應該問本身三個問題:它是不是對稱的、傳 遞的、一致的? 若是答案是否認的,那麼請找到 這些特性未能知足的緣由,再修改equals方法的代碼
equals()和hashCode()同時覆寫
尤爲強調當一個對象被看成鍵值(或索引)來使用的時候要重寫這兩個方法;
覆寫equals後,兩個不一樣實例可能在邏輯上相等,可是根據Object.hashCode方法卻產生不一樣的散列碼,違反「相等的對象必須具備相等的散列碼」。
致使,當你用其中的一個做爲鍵保存到hashMap、hasoTable或hashSet中,再以「相等的」找另 一個做爲鍵值去查找他們的時候,則根本找不到
不一樣類型的hashCode取值
若是該域是布爾型的,計算(f?0:1)
若是是char,short,byte或int,計算(int)f
若是是long類型,計算(int)(f^(f>>>32))
若是是float類型,計算Float.floatToIntBits(f)
若是是double類型,計算Dobule.doubleToLongBits(f)
若是該域是一個對象引用,遞歸調用hashCode
若是該域是一個數組,則把每一個元素當作單獨的域來處理,對每一個重要的元素計算一個散列碼,
Map集合比較:
HashMap的存入順序和輸出順序無關。
LinkedHashMap 則保留了鍵值對的存入順序。
TreeMap則是對Map中的元素進行排序。
由於HashMap和LinkedHashMap 存儲數據的速度比直接使用TreeMap 要快,存取效率要高。
當完成了全部的元素的存放後,咱們再對整個的Map中的元素進行排序。這樣能夠提升整個程序的運行的效率,縮短執行時間。
注意:TreeMap中是根據鍵(Key)進行排序的。而若是咱們要使用TreeMap來進行正常的排序的話,Key 中存放的對象必須實現Comparable 接口。
Map經常使用方法:
Object put(Object key,Object value):用來存放一個鍵-值對Map中
Object remove(Object key):根據key(鍵),移除鍵-值對,並將值返回
void putAll(Map mapping) :將另一個Map中的元素存入當前的Map中
void clear() :清空當前Map中的元素
Object get(Object key) :根據key(鍵)取得對應的值
boolean containsKey(Object key) :判斷Map中是否存在某鍵(key)
boolean containsValue(Object value):判斷Map中是否存在某值(value)
public Set keySet() :返回全部的鍵(key),並使用Set容器存放
public Collection values() :返回全部的值(Value),並使用Collection存放
public Set entrySet() :返回一個實現 Map.Entry 接口的元素 Set
集合遍歷
一、加強for循環 for(Obj o:c){syso(o)}
二、使用iterator , Iterator it=c.iterator;
while(it.hasNext()){Object o = it.next()}
三、普通循環:for(Iterator it=c.iterator();it.hasNext();){it.next() }
代碼實例
總結與面試
1.ArrayList: 元素單個,效率高,多用於查詢
2.Vector: 元素單個,線程安全,多用於查詢
3.LinkedList:元素單個,多用於插入和刪除
4.HashMap: 元素成對,元素可爲空
5.HashTable: 元素成對,線程安全,元素不可爲空
HashMap和Hashtable的區別:
HashMap和Hashtable都是java的集合類,均可以用來存放java對象,這是他們的相同點
如下是他們的區別:
1.歷史緣由:
Hashtable是基於陳舊的Dictionary類的,HashMap是java 1.2引進的Map接口的一個現實。
2.同步性:
Hashtable是同步的,這個類中的一些方法保證了Hashtable中的對象是線程安全的,而HashMap則是異步的,所以HashMap中的對象並非線程安全的,由於同步的要求會影響執行的效率,因此若是你不須要線程安全的結合那麼使用HashMap是一個很好的選擇,這樣能夠避免因爲同步帶來的沒必要要的性能開銷,從而提升效率,咱們通常所編寫的程序都是異步的,但若是是服務器端的代碼除外。
3.值:
HashMap可讓你將空值做爲一個表的條目的key或value
Hashtable是不能放入空值(null)的
ArrayList和Vector的區別:
ArrayList與Vector都是java的集合類,都是用來存放java對象,這是他們的相同點,
區別:
1.同步性:
Vector是同步的,這個類的一些方法保證了Vector中的對象的線程安全的,而ArrayList則是異步的,所以ArrayList中的對象並不 是線程安全的,由於同步要求會影響執行的效率,因此你不須要線程安全的集合那麼使用ArrayList是一個很好的選擇,這樣能夠避免因爲同步帶來的沒必要 要的性能開銷。
2.數據增加:
從內部實現的機制來說,ArrayList和Vector都是使用數組(Array)來控制集合中的對象,當你向兩種類型中增長元素的時候,若是元素的數目超過了內部數組目前的長度他們都須要擴展內部數組的長度,Vector缺省狀況下自動增加原來一倍的數組長度,ArrayList是原來的50%,因此最後你得到的這個集合所佔的空間老是比你實際須要的要大,因此若是你要在集合中保存大量的數據,那麼使用Vector有一些優點,由於你能夠經過設置集合的初始大小來避免沒必要要的資源開銷。
總結:
1)若是要求線程安全,使用Vector,Hashtable
2)若是不要求線程安全,使用ArrayList,LinkedList,HashMap
3)若是要求鍵值對,則使用HashMap,Hashtable
4)若是數據量很大,又要求線程安全考慮Vector
arraylist和linkedlist聯繫與區別
1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。
2.對於隨機訪問get和set,ArrayList以爲優於LinkedList,由於LinkedList要移動指針。
3.對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。 這一點要看實際狀況的。若只對單條數據插入或刪除,ArrayList的速度反而優於LinkedList。但如果批量隨機的插入刪除數據,LinkedList的速度大大優於ArrayList. 由於ArrayList每插入一條數據,要移動插入點及以後的全部數據。
HashMap與TreeMap聯繫與區別
一、 HashMap經過hashcode對其內容進行快速查找,而TreeMap中全部的元素都保持着某種固定的順序,若是你須要獲得一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。
二、在Map 中插入、刪除和定位元素,HashMap是最好的選擇。但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好。使用HashMap要求添加的鍵類明肯定義了hashCode()和 equals()的實現。
兩個map中的元素同樣,但順序不同,致使hashCode()不同。
一樣作測試:
在HashMap中,一樣的值的map,順序不一樣,equals時,false;
而在treeMap中,一樣的值的map,順序不一樣,equals時,true,說明,treeMap在equals()時是整理了順序了的。
https://mp.weixin.qq.com/s/31fbwBmeFYn_yzQVx8520g