日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java程序设计4——集合类

發布時間:2023/12/1 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java程序设计4——集合类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 JAVA集合概述 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

  Java集合封裝了一系列數據結構比如鏈表、二叉樹、棧、隊列等,然后提供了針對這些數據結構的一系列算法比如查找、排序、替換,使編程難度大大降低。(這句話有可能是非法的,因為個人對算法目前不是太了解,并不了解Java有沒有實現哪些數據結構。但是說在這里想給那些畏難算法與數據結構這門課程的人一絲信心,嫑以為非要懂算法和數據結構才能編程,不懂這門課程編也能編程,并不是所有的問題都要自己來實現一個數據結構。如果因為覺得編程一定要懂算法和數據結構,進而對編程產生一種心理陰影,沒有信心,那是很錯誤的!你總需要先用一門語言來練練手,然后才能去學習算法和數據結構。"懂算法和數據結構=會編程",完全是那些所謂的專家來嚇唬那些新手入門者的,找一本語言介紹書好好練練,編程并不是你想的那么難!)這樣的話,只需要利用提供的數據結構存儲需要用的數據,然后利用數據結構上提供的算法就可以對數據進行簡單的處理。鏈表、二叉樹、棧、隊列在JAVA里面統稱為集合,就是把數據統統塞進這些集合里面,這些集合的結構可能是不同的。Java集合將這些數據結構分成如下幾類。

注意: 這些集合類里面存儲的數據都是指向堆內存對象的對象棧內存變量

區別:

  Java的集合類分為四種:Set,List、Map和Queue四種體系,即MSQL設計者可以根據需要使用這四種體系里面的數據結構,然后處理數據。
  Map:代表有映射關系的集合,也就是key-value這樣的數據集合,比如{001——張三,002——李四}
  Set:代表無序、不可重復的集合,比如{1,2,3}。注意:無序、不可重復
  Queue:代表一種隊列集合的實現
  List:代表添加時候有序存儲可重復的集合{1,1,2,3}

1.1 Java集合繼承樹 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

  在處理數據時候經常處理到元素個數不確定(數組長度是固定的,初始化后就無法改變),有關聯的數據{001——張三,002——李四},為了解決這些問題,Java提供了集合類,集合類負責保存、盛裝這些數據,因此集合類也被稱為容器類,所有的集合類都存放在java.util包中
  集合類和數組的區別:
  數組可以保存基本數據類型或者對象,但是集合類智能保存指向堆內存數據的棧內存對象變量(不過很多書上會直接說集合類里面裝的都是對象,這樣可能會好理解一些)。
  MSQL接口類的繼承拓撲圖:
  提供的四大接口類有如下繼承結構的拓撲圖,Collection和Map是兩個根類,由這兩個根類派生出一系列的接口類。

  其中Collection派生SQL,Map派生其他的接口類。MSQL(記法M-SQL,像一種編程語言一樣):Map,Set,Queue,List這四種集合類最常用的是SML(記法:AI領域的一種語言),也就是Set,Map,List,這三種接口類最常用實現是,Set的HashSet,Map的HashMap,List的ArrayList,也就是HHA(2HA,2哈——啊哈)如下圖的顏色重的標注出來是常用的

?

?

1.2 Collection和Iterator接口 ? ? ? ? ? ? ? ? ? ? ??

1.2.1 Collection 接口

  下面如果沒有特別說明,SQL并不指數據庫語言SQL,而是代表Set,Queue,List三個Collection的子接口類。

Collection接口下面定義了若干方法,這些方法可以用于SQL三種集合接口類。注意:集合接口模板類里面存儲的是棧對象變量
boolean add(Object o):該方法用于像一個集合接口類添加一個對象變量o指向的對象,如果添加成功,也就是集合類被改變成功,返回true,否則返回false

boolean addAll(Collection c):假設調用該接口的是集合接口類a,調用該接口后,會將集合接口類c的對象全部添加到集合接口類a中,元素間用逗號隔開

void clear():清空集合里面的所有元素,使元素個數size為0

boolean contains(Object o):檢測集合里面是否含有對象元素o,有true,無false

boolean containsAll(Collection c):假定調用該接口的是集合a,那么調用該接口后會檢測集合a里面是否包含集合c的全部元素,有true,無false

boolean isEmpty():返回集合是否為空,相當于int size()方法返回值為0和不為0的情況

boolean remove(Object o):刪除集合里面指定元素o,刪除成功返回true,失敗false

boolean removeAll(Collection c):假設調用該接口的是集合a,調用該接口后,會從集合a里面找出集合c里面沒有的元素,也就是找出差集,如果刪除了一個或一個以上的元素,該方法返回true

boolean retainAll(Collection c):假設調用該接口的是集合a,調用該接口后,刪除a中c沒有的元素,也就是把a編程a和c的交集,如果操作成功,返回true

int size():返回集合里面元素個數

Object[] toArray():把集合轉換成一個數組,所有的集合元素變成對應的數組元素,這樣就可以用數組的方法來訪問元素

Iterator iterator():返回一個Iterator對象用于遍歷集合里面的所有元素,也就是一個迭代器,這個迭代器可以用于查詢集合類元素,利用System.out的println查詢同樣可以打印出集合的所有數據,但是這樣的查詢是不可控制的,也就是說要么查詢出所有,要么不查詢,使用迭代器可以根據需要進行查詢。

1.2.2 Iterator接口

  Iterator接口隱藏了Collection集合類的底層實現,提供了若干方法對Collection集合類進行處理。
方法:

boolean hasNext():如果被迭代的集合仍然有元素沒有被遍歷(迭代),則返回true,就是說如果集合里面迭代一次后剩余的元素不止一個,則返回true。
Object next():返回集合里下一個元素,注意迭代器每次只返回一個,不像println那樣一次返回所有
void remove():刪除集合里上一次next方法返回的元素,比如說如果等于某個值,就可以刪除這個元素,這樣就可以控制集合返回結果
看一段代碼:
?

1 //創建一個集合 2 Collection books = new HashSet(); 3 books.add("輕量級J2EE企業應用實戰"); 4 books.add("Struts2權威指南"); 5 books.add("基于J2EE的Ajax寶典"); 6 //獲取books集合對應的迭代器 7 Iterator it = books.iterator(); 8 while(it.hasNext()){ 9 //it.next()方法返回的數據類型是Object類型,需要強制類型轉換 10 String book = (String)it.next();//book代表的是每次返回的一個元素 11 // it.remove();//remove刪除每次next方法返回的元素,按照此循環,如果每次返回后都進行刪除,那么最后就不返回任何一個結果,因為返回一個刪掉一個 12 //如果next方法返回的元素與Struts2權威指南一樣,則刪除 13 // if(book.equals("Struts2權威指南")){ 14 // 15 // it.remove(); 16 // } 17 //對book變量賦值,不會改變改變集合本身 18 // System.out.println(book); 19 //book = "測試字符串"; 20 } 21 System.out.println(books);

?強烈注意

  Iterator接口類僅用于遍歷集合,Iterator本身并不提供盛裝對象的能力,如果需要創建Iterator對象,則必須與有一個可以被它迭代的集合,沒有集合的迭代器沒有存在價值。也就是說Iterator必須依附于Collection對象,有一個Iterator對象,則必然有一個與之關聯的Collection對象供其迭代。
代碼倒數第二行有一個book賦值代碼,但是輸出集合books時候,輸出結果沒有任何改變,可以得到一個結論:當使用Iterator對集合進行迭代輸出時候,Iterator并沒有指向堆內存的集合,而是把集合元素的值傳給了迭代變量,所以修改迭代變量的值對集合元素沒有任何改變。

墻裂注意

1. 刪除集合里面的元素只能通過迭代器的remove方法,也就是it.remove()才可以刪除集合里面的元素,通過調用集合自身的方法來刪除集合元素值也就是說books.remove(book)將會出錯,引發的異常是java.util.ConcurrentModificationException異常,或者簡單的說,迭代過程中,不能通過除了迭代器之外的方式來修改集合,也就是說在迭代過程中,只有迭代器有修改集合的權限,其他方式包括集合本身都沒有修改集合自身元素的權限。(其實更本質的是集合類變量本身只是指向堆內存的數據,迭代時候,相當于堆內存的使用權交給了迭代器,集合類本身當然沒有修改權限)迭代器采用的是快速失敗機制,一旦在迭代過程中,檢測到該集合已經被修改(通常是其他線程修改),程序立即引發ConcurrentModificationException異常,而不是顯示修改后的結果,這樣可以避免共享資源而引發的潛在問題。

第二for循環遍歷集合元素

前面已經介紹了for循環有兩種方是,一種是標準的,另一種是被很多書稱之為foreach循環(這種叫法很容易讓人誤解循環的關鍵字是foreach,但其實依然是for),這里我稱之為第二for循環(表示第二種for循環),同樣使用在用for循環迭代輸出集合類“中”也不能修改集合元素。同樣使用該種循環方式按照前面的語法
for(循環變量類型 循環變量:集合類)

代碼如下:

1 //創建一個集合 2 Collection lan = new HashSet(); 3 //往集合里面裝東西 4 lan.add("Englis"); 5 lan.add("Chinese"); 6 lan.add("Castellano"); 7 lan.add("Deutsch"); 8 //使用第二for循環輸出集合里面的元素 9 for(Object lang:lan){ 10 String lgu = (String)lang;//集合里面存儲的都是對象變量 11 System.out.print(lgu); 12 }

2 Set集合類 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

Set集合特點:Set集合接口類的元素特征是無序、不重復

  Set集合與Collection基本完全一樣,它沒有提供任何額外的方法,實際上Set就是Collection,只是行為不同(Set不允許重復元素)。
  Set不允許重復元素,如果相同元素加入同一個Set集合中則會添加失敗,判斷是否相同的標準是使用equals方法,而不是==方法,所以比較嚴格。

例如:

1 Set books = new HashSet(); 2 books.add(new String("English")); 3 //再次添加一個不同的對象 4 books.add(new String("English"));

由于Set是一個集合類,集合類里面應該裝的是不同對象,上述代碼里面明顯裝的是不同對象,但是Set集合判斷是否是同一個對象的標準是equals方法,所以不能添加(有點怪怪的)

上面介紹的是Set集合的通用知識,因此完全適合后面介紹的HashSet、TreeSet、EnumSet三個實現類,只是三個實現類各有特色。

2.1 HashSet——LinkedHashSet類 ? ??

2.1.1 HashSet接口 ? ? ? ? ? ? ? ? ? ??

  HashSet是Set接口的典型實現,大多數時候使用Set集合就是使用這個實現類,HashSet按Hash算法存儲集合中的元素,因此具有很好的存取和查找性能。
HashSet特點
  1. 不能保證元素的排列順序,順序有可能發送變化
  2. HashSet不是同步的,如果多個線程同時訪問一個Set集合或者一個HashSet,當2條或2條以上的線程同時修改了HashSet集合時,必須通過代碼來保證同步。
  3. 集合元素值可以是null

  當向HashSet集合中存入一個元素,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然后根據hashCode值來決定該對象在HashSet中存儲位置。如果有兩個元素通過equals方法比較返回true,但他們的hashCode()方法返回值不相等,HashSet會把他們存儲在不同位置,也就可以添加成功。
簡單的說:HashSet集合通過兩個條件來判斷是否存儲要插入的元素。一個是通過equals判斷元素值是否相等,另一個是通過hashCode()方法判斷Hash值是否相等。只有兩個都相等了,才說明要插入的元素相等。

1 import java.util.*; 2 /** 3 * Description: 4 * <br/>Copyright (C), 2005-2008, Yeeku.H.Lee 5 * <br/>This program is protected by copyright laws. 6 * <br/>Program Name: 7 * <br/>Date: 8 * @author Yeeku.H.Lee kongyeeku@163.com 9 * @version 1.0 10 */ 11 12 //類A的equals方法總是返回true,但沒有重寫其hashCode()方法 13 class A{ 14 public boolean equals(Object obj){ 15 return true; 16 } 17 } 18 //類B的hashCode()方法總是返回1,但沒有重寫其equals()方法 19 class B{ 20 public int hashCode(){ 21 return 1; 22 } 23 } 24 //類C的hashCode()方法總是返回2,但沒有重寫其equals()方法 25 class C{ 26 public int hashCode(){ 27 return 2; 28 } 29 public boolean equals(Object obj){ 30 return true; 31 } 32 } 33 public class TestHashSet{ 34 public static void main(String[] args) { 35 HashSet books = new HashSet(); 36 //分別向books集合中添加2個A對象,2個B對象,2個C對象 37 books.add(new A()); 38 books.add(new A()); 39 books.add(new B()); 40 books.add(new B()); 41 books.add(new C()); 42 books.add(new C()); 43 System.out.println(books); 44 } 45 }

  上面的程序中向books集合中添加了2個A對象、2個B對象、2個C對象,其中C類重寫了equals()方法,總是返回true,hashCode()方法總是返回2,這將導致HashSet會把2個C對象當成同一個對象。運行上述可以得到
[B@1,B@1,C@2,A@343,A@kd33]
需要注意的是
  如果需要把一個對象放入HashSet中時,如果重寫該對象對應類的equals()方法,也應該重寫起hashCode()方法,其規則是:如果2個對象通過equals方法比較返回true,這兩個hashCode應該也相同,這樣就不會添加兩個同樣的對象。
  如果兩個對象通過equals方法返回true,但是通過hashCode返回不同的,那么將會將兩個對象保存在不同位置,從而都可以添加成功,這樣會違反Set集合的特征(雖然可以添加成功)。
  如果equals方法返回false,但hashCode值卻是一樣的,這樣更加極品。因為hash值一樣,HashSet試圖把它們保存在同一個位置(但實際不能這樣做,否認則將覆蓋其中一個),所以處理比較復雜,而且HashSet訪問元素根據hash值訪問,如果HashSet中包含元素有相同的hashCode值,將導致性能下降。
  HashSet的使用hashCode值去查詢元素,hashCode就相當于數組索引,但為什么不使用數組呢?因為數組元素索引是連續的,并且長度固定,無法自由增加數組長度。而HashSet訪問元素,可以先算出hashCode值,然后去對應位置取出元素。

?重寫hashCode方法原則:
?當兩個對象通過equals方法返回true時,對象的hashCode應該也要相等
?對象中用作equals比較標準的熟悉,都應該用來計算hashCode值
?重寫hashCode()方式
?第一步:

?對象內每個有意義的熟悉f(即每個用作equals()比較標準的屬性)計算出一個int類型的hashCode值,計算方法如下:
?不同類型屬性取得hashCode值方式
?屬性類型??????計算方式
?boolean???????hashCode=(f?0:1);
?整數類型(byte,short,char,int)?hashCode=(int)f;
?long???????hashCode=(int)(f^(f>>>32))
?float???????hashCode=Float.floatToIntBits(f)
?double???????long l = Double.doubleTolongBits(f);
?hashCode = (int)(l^(l>>>32));
?普通引用類型?????hashCode = f.hashCode();
第二步:

用第一步計算出來的對象的多個hashCode值組合計算出一個最終的hashCode,作為對象的hashCode值返回
return f2.hashCode() + (int)f2;
為了避免直接相加產生的偶然相等(兩個對象的f1、f2不等,但他們的和剛好相等。例如6=2+4=3+3),可以通過為各屬性乘以一個質數再相加:
return f1.hashCode()*17 + (int)f2*13;
當向HashSet中添加可變對象時,必須十分小心,如果修改HashSet集合中的對象,有可能導致該對象與集合中其他對象相等,從而導致HashSet無法準確訪問對象。也就是說添加時候會做出檢查,修改時候是不會檢查元素之間是否相同的。所以如果修改導致了與其他對象相等,那么HashSet無法準確訪問該對象。

2.1.2 LinkedHashSet接口 ? ? ? ? ? ? ? ? ??

  HashSet還有一個子類LinkedHashSet集合,LinkedHashSet集合也是根據元素hashCode值來決定元素的存儲位置,但它同時使用鏈表維護元素次序,這樣時元素看起來以插入的順序保存的,也就是說,當遍歷LinkedHashSet集合元素時,HashSet將會按元素添加順序來訪問集合里的元素。LinkedHashSet需要維護元素的插入順序,因此性能略低于HashSet性能,但迭代輸出時候性能將會很好。

?注意:LinkedHashSet集合特點是有序的,并且順序和添加時候順序一致。

?2.2 SortedSet——TreeSet接口 ? ? ? ??

TreeSet是SortedSet接口的唯一實現,正如SortedSet名字所述,TreeSet可以確保元素處于排序狀態。TreeSet提供了如下方法

方法:?????????????

Comparator comparator():返回當前Set使用的Comparator,或者null(表示自然方式排序)
Object first():返回集合中的第一個元素
Object last():返回集合中的最后一個元素
Object lower(Object e):返回集合中與e相比的最大元素,e不一定是TreeSet里面的元素,也就是下確界
Object higher(Object e):返回集合中與e相比的最小元素,也就是上確界,e不一定是TreeSet里面的元素

SortedSet subSet(fromElement,toElement):返回此Set的子集合,范圍從fromElement(包含)到toElement(不包含)

SortedSet headSet(toElement):返回此Set的子集,由小于toElement的元素組成
SortedSet tailSet(fromElement):返回此Set的子集,由大于等于fromElement的元素組成

總的來說:TreeSet提供了:訪問第一個最后一個,上確界、下確界(前一個后一個),以及截取子TreeSet的方法

區別:????

  同樣是提供了排序的存儲,LinkedHashSet與TreeSet是有本質區別的。LinkedHashSet的順序是插入元素時候的順序,TreeSet提供的是元素值的順序

  TreeSet支持兩種排序方法:自然排序和定制排序,默認是自然排序

2.2.1 自然排序 ? ? ?

  TreeSet會調用集合元素的compareTo(Object obj)方法來比較元素之間大小關系,然后集合元素按照升序排列,這種方式就是自然排序。
  Java提供了一個Comparable接口,該接口定義一個compareTo(Object obj)方法,該方法返回一個整數值,實現該接口的類必須實現該方法,實現了該接口的類的對象就可以比較大小。當一個對象調用該方法與另一個對象進行比較,例如obj1.compareTo(obj2),如果該方法返回0,則表示這兩個對象相等;如果返回一個正整數,則表明obj1大于obj2,如果返回是一個負整數,則表示obj1小于obj2。

  Java的一些常用類已經實現了Comparable接口,并提供了比較大小的標準。下面是實現了Comparable接口的常用類。
  BigDecimal、BigInteger以及所有數值型對應的包裝類:按它們對應的數值大小進行比較
  Character:按字符的UNICODE值進行比較
  Boolean:true對應的包裝類實例大于false對應的包裝類實例
  String:按字符串字符的UNICODE值進行比較
  Date、Time:后面的時間、日期比前面的時間、日期大。
  如果試圖把一個對象添加進TreeSet時,該對象的類必須實現Comparable接口,否則程序拋出異常。另外在實現compareTo(Object obj)方法時,需要將被比較對象obj強制類型轉換成相同類型,因為只有相同類的實例才會比較大小。比如日期和字符串就不能直接比較。
  對TreeSet集合而言:判斷兩個對象不相等的標準是:兩個對象通過equals方法比較false,或通過compareTo(Object obj)比較沒有返回0——即使兩個對象是同一個對象。
  類似地:當需要把一個對象放入TreeSet中時,重寫該對象對應類的equals()方法時,應該保證該方法與compareTo(Object obj)方法有一致的結果,其規則是:如果兩個對象通過equals方法比較返回true時,這兩個對象通過compareTo(Object obj)方法比較返回0.

  如果兩個對象通過equals方法返回true,但是通過compareTo(Object obj)返回不同的,那么將會將兩個對象保存在不同位置,從而都可以添加成功,這樣會違反Set集合的特征(雖然可以添加成功)。
  如果equals方法返回false,但compareTo(Object obj)值卻是一樣的,這樣更加極品。因為比較相等,TreeSet試圖把它們保存在同一個位置(但實際不能這樣做,否認則將覆蓋其中一個),所以處理比較復雜。
  如果向TreeSet中添加一個可變對象后,并且后面程序修改了該可變 對象的屬性,導致它與其他對象的大小順序發生了改變,但TreeSet不會再次調整它們的順序,甚至可能導致TreeSet中保存這兩個對象,它們通過equals方法比較返回true,通過compareTo(Object obj)方法比較返回0.

2.2.2 定制排序 ? ? ??

  可以對TreeSet集合類進行自定義排序,比如降序。可以使用Comparator接口幫助,該接口里面有一個int compare(T o1,T o2)方法用于比較o1和o2大小,比較原理痛compareTo()一樣。
  如果實現定制排序,需要在創建TreeSet集合對象時,提供一個Comparator對象與該TreeSet集合關聯,由該Comparator對象負責集合元素的排序邏輯。

2.3 EnumSet類 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

  EnumSet是一個專為枚舉類設計的集合類,EnumSet中所有值都必須是指定枚舉類型的枚舉值,該枚舉類型在創建EnumSet時顯式或隱式地指定。EnumSet集合元素也是有序的,EnumSet以枚舉值在Enum類定義順序來決定集合元素的順序。也就是不以元素值大小決定,根據添加順序決定。
  EnumSet在內部以為向量的形式存儲,這種存儲形式緊湊、高效,占用內存小。
  EnumSet集合不允許加入null元素,如果插入null元素將拋出空指針異常。如果只是測試是否出現null元素或刪除null元素都不會拋出異常,只是刪除操作將返回false,因為沒有任何null元素被刪除。
?  EnumSet類沒有暴露任何構造器來創建該類的實例,程序應該通過它提供的static方法來創建EnumSet對象。它提供了如下常用static方法來創建EnumSet對象。

2.3.1 EnumSet創建方法 ? ? ? ? ??

static EnumSet allOf(Class elementType):創建一個包含指定枚舉類里所有枚舉值的EnumSet集合
static EnumSet complementOf(EnumSet s):假設調用該方法的是EnumSet a,調用后的新EnumSet的集合元素是a+s,結果放在a中
static EnumSet copyOf(Collection c):使用一個普通集合來創建EnumSet集合
static EnumSet copyOf(EnumSet s):創建一個與指定EnumSet具有相同元素類型、相同集合元素的EnumSet
static EnumSet noneOf(Class elementType):創建一個元素類型為指定枚舉類型的空EnumSet
static EnumSet of(E first,E...rest):創建一個包含一個或多個枚舉值的EnumSet,傳入的多個枚舉值必須屬于同一個枚舉類。
static EnumSet range(E from ,E to):創建包含從from枚舉值,到to枚舉值范圍內所有枚舉值的EnumSet集合

注意:

EnumSet復制另一個EnumSet集合中所有元素創建新的EnumSet,或復制另一個Collection集合中所有元素來創建新的EnumSet,當復制Collection集合中所有元素創建新的EnumSet時,要求Collection集合中所有元素必須是同一個枚舉類型的枚舉值

  HashSet和TreeSet是Set的兩個典型實現,那如何選擇HashSet和TreeSet呢?HashSet的性能總是比TreeSet好(特別是最常用的添加、查詢元素等操作),因為TreeSet需要額外的紅黑樹算法來維護集合元素的次序。只有當需要一個保持排序的Set時,才應該使用TreeSet,否則都應該使用HashSet
  HashSet還有一個子類:LinkedHashSet,對于普通插入、刪除操作,LinkedHashSet比HashSet要略慢一點:這是有維護鏈表所帶來的額外開銷,不過有了鏈表,遍歷LinkedHashSet會更快
  EnumSet是所有Set實現類中性能最好的,但它只能保存同一個枚舉類的枚舉值作為集合元素。
  必須指出的是,Set的三個實現類HashSet、TreeSet和EnumSet都是線程不安全的,如果有多條線程同時訪問一個Set集合,并且有超過一條線程修改了該Set集合,則必須手動保證該Set集合的同步性。通常可以通過Collections工具類synchronizedSortedSet方法來包裝該Set集合。次操作最好在創建時進行,以防止對Set集合的意外非同步訪問。

3.Queue集合類 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

?

3.1概述 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

  Queue用于模擬了隊列這種數據結構,隊列是指"先進先出"(FIFO)的容器。隊列的頭部保存在隊列中時間最長的元素,隊列的尾部保存在隊列中時間最短的元素。

3.2隊列 ? ? ? ? ? ? ? ? ? ? ? ? ? ??

  隊列是一種特殊的線性表,它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。隊列中沒有元素時,稱為空隊列。
  在隊列這種數據結構中,最先插入的元素將是最先被刪除的元素;反之最后插入的元素將是最后被刪除的元素,因此隊列又稱為“先進先出”(FIFO—first in first out)的線性表。
  隊列空的條件:front=rear
  隊列滿的條件: rear = MAXSIZE
  隊列不允許隨機訪問隊列中的元素。

3.3 Queue接口定義的方法 ? ? ? ? ? ? ? ?

  void add(Object e):將制定元素加入此隊列的尾部。
  Object element():獲取隊列頭部的元素,但是不刪除該元素。
  boolean offer(Object e):將指定元素加入此隊列的尾部。當使用有容量限制的隊列時,此方法通常比add(Object e)方法更好。
  Object peek():獲取隊列頭部的元素,但是不刪除該元素,如果此隊列為空,則返回null
  Object poll():獲取隊列頭部的元素,并刪除該元素,如果隊列為空,則返回為null
  Object remove():獲取隊列頭部的元素,并刪除該元素。

3.4 Queue的實現類 ? ? ? ? ? ? ? ? ? ? ? ? ?

Queue有兩個常用的實現類:LinkedList和PriorityQueue,下面介紹著兩個類。

3.4.1 LinkedList實現類 ? ?

  LinkedList類是一個奇怪的類,它是List接口的實現類——這意味著它是一個List集合,可以根據索引來隨機訪問集合中的元素。除此之外,LinkedList還實現了Deque接口,Deque接口是Queue接口的子接口,它代表一個雙向隊列,Deque有如下方法。
void addFirst(Object e):將制定元素插入該雙向隊列的開頭
void addLast(Object e):將制定元素插入該雙向隊列的末尾
Iterator descendingIterator():返回以該雙向隊列對應的迭代器,該迭代器將以逆向順序來迭代隊列中的元素。
Object getFirst():獲取、但不刪除雙向隊列的第一個元素
Object getLast():獲取、但不刪除雙向隊列的最后一個元素
boolean offerFirst(Object e):將指定的元素插入該雙向隊列的開頭
boolean offerLast(Object e):將指定的元素插入該雙向隊列的末尾
Object poolFirst():獲取、并刪除該雙向隊列的第一個元素;如果此雙向隊列為空,則返回null
Object poolLast():獲取、并刪除該雙向隊列的最后一個元素;如果此雙向隊列為空,則返回null
Object peekFirst():獲取、但不刪除該雙向隊列的第一個元素;如果此雙向隊列為空,則返回null
Object peekLast():獲取、但不刪除該雙向隊列的最后一個元素;如果此雙向隊列為空,則返回null
Object pop():pop出該雙向隊列所表示的棧中第一個元素
void push(Object e):將一個元素push進該雙向隊列所表示的棧中(即該雙向隊列的頭部)
Object removeFirst():獲取并刪除該雙向隊列的第一個元素
Object removeFirstOccurrence(Object o):刪除該雙向隊列的第一次出現元素o
removeLast():獲取并刪除該雙向隊列的最后一個元素
removeLastOccurrence(Object o):刪除該雙向隊列的最后一次出現的元素o

LinkedList不僅可以當成雙向隊列使用,也可以當成"棧"(但其實不是棧)使用,因為該類里面有pop和push兩個方法,除此之外,LinkedList實現了List接口,所以被當成List使用。

  LinkedList有上述方法讓它可以當做雙向隊列、棧和List集合用。LinkedList是個相當強大的集合類。
  LinkedList與ArrayList、Vector的實現機制完全不同,ArrayList、Vector內部以數組形式保存集合中的元素,因此隨機訪問集合元素上性能較好,而LinkedList以鏈表形式保存集合,所以隨機訪問集合元素較差,但插入、刪除元素時性能出色。
  通常編程時候無需理會ArrayList和LinkedList性能差異,只需要知道LinkedList集合不僅提供了List功能,還有雙向隊列及棧功能,在一些性能比較敏感的地方,可能需要慎重選擇哪個List實現。下面是三者性能差異:
        類別        實現機制        隨機訪問排名     迭代排名   插入排名   刪除排名
        數組      連續內存區保存元素        1        ?不支持      不支持   不支持
        ArrayList   內部以數組保存元素       ?2          2       ?2      2
        Vector    內部以數組保存元素        3          3      ? ?3     3
        LinkedList  內部以鏈表保存元素      ?4         ?1       ? 1     1
  從上表看出:因為數組以一塊連續內存區保存所有數組元素,所以數組在隨機訪問時性能最好。所有內部數組作為底層實現的集合在隨機訪問時也有較好的性能;而內部以鏈表作為底層實現的集合在插入、刪除操作時有很好的性能,以鏈表作為底層實現的集合也比數組作為底層實現的集合性能好。

1 package chapter3excercise; 2 3 import java.util.*; 4 5 public class TestPerformance { 6 public static void main(String[] args){ 7 //創建一個字符串數組 8 String[] test1 = new String[200000]; 9 //動態初始化數組 10 for(int i = 0;i < test1.length;i++){ 11 test1[i] = String.valueOf(i); 12 } 13 ArrayList al = new ArrayList(); 14 for(int i = 0;i < test1.length;i++){ 15 al.add(test1[i]); 16 } 17 LinkedList ll = new LinkedList(); 18 for(int i = 0;i < test1.length;i++){ 19 ll.add(test1[i]); 20 } 21 long start = System.currentTimeMillis(); 22 for(Iterator it = al.iterator();it.hasNext();){ 23 it.next(); 24 } 25 System.out.println("迭代ArrayList元素所需要的時間:" + 26 (System.currentTimeMillis() - start)); 27 start = System.currentTimeMillis(); 28 for(Iterator it = ll.iterator();it.hasNext();){ 29 it.next(); 30 } 31 System.out.println("迭代LinkedList元素所需要的時間:" + 32 (System.currentTimeMillis() - start)); 33 } 34 }

  多次運行上面程序會發現,迭代ArrayList集合的時間略大于迭代LinkedList集合的時間。因此,關于使用List集合有如下建議:
  如果需要遍歷List集合元素,對于ArrayList、Vector集合,則應該使用隨機訪問方法(get)來遍歷集合元素,這樣性能更好。對于LinkedList集合,則應該采用迭代器來遍歷集合元素
  如果需要經常執行插入、刪除操作來改變List集合的大小,則應該使用LinkedList集合,而不是ArrayList。使用ArrayList、Vector集合將需要經常重新分配內部數組的大小,其時間開銷常常是使用LinkedList時時間開銷的幾十倍,效果很差。
  如果有多條線程需要同時訪問List集合中元素,可以考慮使用Vector這個同步實現。

3.4.2 PriorityQueue實現類 ? ?

  PriorityQueue是一個比較標準的隊列實現類,之所以說它是比較標準的隊列實現,而不是絕對標準的隊列實現是因為:PriorityQueue保存隊列元素的順序并不是按加入隊列的順序,而是按隊列元素的大小進行重新排序。因此因此當調用peek方法或者poll方法來取出隊列中的元素時,并不是取出最先進入隊列的元素,而是取出隊列最小的元素。從這個意義上看,PriorityQueue已經違反了隊列的最基本的規則:先進先出。

  PriorityQueue不允許插入null元素,它還需要對隊列元素進行排序,隊列元素有兩種排序方式:
自然排序:采用自然順序的PriorityQueue集合中的元素必須實現了Comparable接口,而且應該是同一類的多個實例,否則可能導致ClassCastException異常
定制排序:創建PriorityQueue隊列時,傳入一個Comparator對象,該對象負責對隊列中所有元素排序,采用定制排序時不要求隊列元素實現Comparable接口
PriorityQueue隊列對元素的要求與前面TreeSet對元素的要求基本一致,可以參考TreeSet處理

4.List接口和ListIterator接口 ? ? ? ? ? ? ? ? ? ? ? ? ? ??

  List作為Collection接口的子接口,可以使用Collection接口里的全部方法,而且List是有序集合,因此List集合里面增加了一些根據索引來操作集合元素的方法:
  void add(int index,Object element):將元素element插入在List集合index處
  boolean addAll(int index,Collection c):將集合c的所有元素都插入在List集合index處
  Object get(int index):返回集合index索引處的元素
  int indexOf(Object o):返回對象o在List集合中出現的位置索引
  int lastIndexOf(Object o):返回對象o在List集合中最后一次出現的位置索引
  Object remove(int index):刪除并返回index索引處的元素
  Object set(int index ,Object element):將index索引處的元素替換成element對象,返回新元素
  List subList(int fromIndex,int toIndex):返回從索引fromIndex(包含)到索引toIndex(不包含)處所有集合元素的子集合。
  所有List實現類都可以調用這些方法實現對集合元素的操作,相對于Set集合,List可以根據索引來插入、替換和刪除集合元素。
  與Set只提供了一個iterator()方法不同,List還額外提供了一個listIterator()方法,該方法返回一個ListIterator對象,ListIterator接口繼承了Iterator接口,提供了專門操作List的方法。

  ListIterator接口在Iterator接口基礎上增加了如下方法:
  boolean hasPrevious():返回該迭代器關聯的集合是否還有上一個元素
  Object previous():返回該迭代器的上一個元素
  void add():在指定位置插入一個元素
  拿ListIterator與普通Iterator進行對比,容易發現ListIterator增加了向前迭代的功能(Iterator只能向后迭代),而且ListIterator還可以通過add方法向List集合添加元素(Iterator只能刪除元素)。
  正向迭代是從正向迭代輸出元素,反向迭代是從反向迭代輸出元素。

4.1 ArrayList和Vector實現類 ? ? ? ? ??

  ArrayList和Vector是List的兩個典型實現類,完全支持List接口的全部功能。
  ArrayList和Vector類是基于數組實現的List類,所以ArrayList和Vector類封裝了一個動態再分配的Object[]數組。每個ArrayList或Vector對象有一個capacity屬性,這個capacity表示它們所封裝的Object[]數組長度。當想ArrayList或Vector中添加元素時,其capacity會自動增加。
  通常無需關心ArrayList或Vector的capacity屬性,但如果想ArrayList集合或Vector集合中添加大量元素時,可以使用ensureCapacity方法一次性增加capacity,可以減少增加分配次數,提高性能。
  如果開始就知道ArrayList集合或Vector集合需要保存多少個元素,可以創建時候就指定capacity大小。如果不指定,則capacity屬性默認值為10
此外,ArrayList和Vector集合提供了兩個方法操作capacity屬性。
  void ensureCapacity(int minCapacity):將ArrayList或Vector集合中的capacity增加minCapacity
  void trimToSize():調整ArrayList或Vector集合的capacity為列表當前大小。程序可調用該方法來減少ArrayList或Vector集合對象存儲空間
  ArrayList或Vector在用法上幾乎完全相同,但Vector是一個古老的集合最開始Java沒有提供系統的集合框架,所以Vector提供了一些方法名很長的方法。   ArrayList開始就作為List的主要實現類,因此沒有那些方法名很長的方法,實際上Vector有很多缺點,通常盡量少用Vector實現類。
  ArrayList和Vector的顯著區別是ArrayList是線程不安全的,當多線程訪問同一個ArrayList集合時,如果超過一條修改了ArrayList集合,則需要手動保證集合的同步性,而Vector集合是線程安全的,無需保證集合的同步性。Vector性能低于ArrayList性能。
  Vector還提供了Stack子類,用于模擬"棧"這種數據結構,棧通常是后進先出進棧出棧的都是Object,所以取出棧里的元素需要做類型轉換。
  Object peek():返回"棧"的第一個元素,但并不將該元素pop出棧
  Object pop():返回棧的第一個元素,并將該元素pop出棧
  void push(Object item):將一個元素push進棧,最后一個進棧的元素總是位于棧頂。
  數組的工具類Arrays里面提供了asList(Object...a)方法,該方法可以把一個數組或指定個數的對象轉換成一個List集合,這個List集合既不是ArrayList實現類,也不是Vector實現類的實例,而是Arrays的內部類ArrayList的實例
  Arrays.ArrayList是一個固定長度的List集合,程序只能遍歷訪問該集合里的元素,不可增加、刪除集合里的元素。

  

5.Map集合????????????????????????????????????????????????????????????????????????

5.1 Map集合特點?????????????????

  Map用于保存具有映射關系的數據,因此Map集合保存著兩組值,一組值用于保存Map里的key,另外一組值用于保存Map里的value,key和value都可以是任何引用類型的數據。Map的key不允許重復,即同一個Map對象的任何兩個key通過equals方法比較總是返回false。key和value之間存在單向一對一關系,即通過指定的key,總能找到唯一的、確定的value。從Map中取出數據時,只要給出指定的key,就可以取出對應的value。也就是說Map保存的元素是鍵值對。如果把Map里的key放在一起看,它們就組成了一個Set集合(key是沒有順序,key與key之間不能重復),事實上Map確實包含了一個keySet()方法,用于返回Map所有key組成的Set集合。

  不僅如此Map里key集合和Set集合里元素的存儲形式也很像,Map子類和Set子類在名字上也幾乎相似。Set接口下有HashSet、LinkedHashSet、SortedSet(接口)、TreeSet、EnumSet等實現類和子接口,而Map接口下則有HashMap、LinkedHashMap、SortedMap(接口)、TreeMap、EnumMap等實現類和子接口。正如名字暗示,Map的實現類和子接口中key集存儲形式和對應的Set集合存儲形式完全相同。如果把Map所有的value放在一起看,它們非常類似于一個List:元素與元素之間可以重復,每個元素通過索引來查找,只是Map中的索引不再使用整數值,而是另一個對象來作為索引。如果需要從List集合取出元素,需要提供該元素的數字索引,如果需要從Map中取出元素,需要提供該元素的key索引,因此Map也被稱為字典或關聯數組。

5.2 Map接口方法???????????????????

  Map中包括一個內部實現類:Entry。該類封裝了一個key-value對,Entry包含三個方法:
  Object getKey():返回該Entry里包含的key值
  Object getValue():返回Entry里面包含的value值
  Object setValue(V value):設置該Entry里包含的value值,并返回新設置的value值
  可以把Map理解成一個特殊的Set,只是該Set里包含的集合元素是Entry類對象,而不是普通對象

5.3Map接口的內部實現類??????

  Map中包括一個內部實現類:Entry。該類封裝了一個key-value對,Entry包含三個方法:   Object getKey():返回該Entry里包含的key值   Object getValue():返回Entry里面包含的value值   Object setValue(V value):設置該Entry里包含的value值,并返回新設置的value值   可以把Map理解成一個特殊的Set,只是該Set里包含的集合元素是Entry類對象,而不是普通對象

5.4?HashMap和Hashtable

  HashMap和Hashtable是Map接口的典型實現類,它們之間的關系類似于ArrayList和Vector關系:Hashtable是個古老的Map實現類,它有兩個繁瑣的方法elements()和keys(),現在基本不用了。

Hashtable和HashMap的區別

  Hashtable是一個線程安全的Map實現,但HashMap是線程不安全的實現,所以HashMap比Hashtable性能好,但多線程訪問Map對象時,Hashtable實現類更好 Hashtable不允許使用null作為key和table,如果把null值放進Hashtable中,將會引發NullPointerException異常,但HashMap可以使用null作為key或value,但由于key不能重復,所以HashMap里最多只有一項key-value對的key為null,但可以有無數多項key-value對的value為null。key類似Set集合的,所以無序、禁止重復key,而value類似List所以可以重復,順序和key是對應的。 代碼:

1 package chapter7; 2 3 import java.util.HashMap; 4 5 public class NullInHashMapTest { 6 public static void main(String[] args){ 7 HashMap hm = new HashMap(); 8 //將兩個key值為null的放在key-value鍵值對中 9 hm.put(null, null); 10 hm.put(null, null); 11 //將一個value值為null的放入key-value鍵值對中 12 hm.put('a', null); 13 System.out.println(hm); 14 } 15 } 16 17 輸出結果:{null=null, a=null}

  根據輸出結果可以看出HashMap重寫了toString()方法,實際所有Map實現類都重寫了toString()方法,調用Map對象的toString()方法總是返回如下格式字符串 {key1=value1,key2=value2}

  Hashtable從類名上就可以看出是一個古老的類,命名甚至都沒有遵守Java的命名規范:每個類的單詞首字母大寫。后來也沒有改成HashTable,否則將有大量程序需要改寫。盡量少用Hashtable類,即使需要創建線程安全的Map實現類,可以通過Collections工具類,把HashMap變成線程安全的,無須使用Hashtable實現類。

  為了成功在HashMap、Hashtable中存儲、獲取對象,用作key的對象必須實現hashCode方法和equals方法。HashMap、Hashtable判斷兩個key相等的標準也是key通過equals方法返回true,兩個key的hashCode相等。判斷value相等的標準是equals返回true即可,不需要hashCode判斷。

1 package chapter7; 2 3 import java.util.Hashtable; 4 5 //定義類A,該類根據A對象的count屬性來判斷兩個對象是否相等,計算hashCode值 6 //只要兩個A對象的count相等,則它們通過equals比較也相等,其hashCode值也相等 7 8 class AA{ 9 int count; 10 public AA(int count){ 11 this.count = count; 12 } 13 public boolean equals(Object obj){ 14 if (obj == this){ 15 return true; 16 } 17 if(obj!=null && obj.getClass() == AA.class){ 18 AA a = (AA)obj; 19 if(this.count == a.count){ 20 return true; 21 } 22 23 } 24 return false; 25 } 26 public int hashCode(){ 27 return this.count; 28 } 29 } 30 //定義類B,B對象與任何對象通過equals方法比較都相等 31 class BB{ 32 public boolean equals(Object obj){ 33 return true; 34 } 35 } 36 public class HashtableTest { 37 public static void main(String[] args){ 38 Hashtable ht = new Hashtable(); 39 ht.put(new AA(60000),"English"); 40 ht.put(new AA(87653),"Castellano"); 41 ht.put(new AA(1232),new B()); 42 System.out.println(ht); 43 //只要兩個對象通過equals比較返回true,Hashtable就認為它們是相等的value。 44 //因為Hashtable中有一個B對象,它與任何對象通過equals比較都相等,所以下面輸出true。 45 System.out.println(ht.containsValue("測試字符串")); 46 //只要兩個A對象的count屬性相等,它們通過equals比較返回true,且hashCode相等 47 //Hashtable即認為它們是相同的key,所以下面輸出true。 48 System.out.println(ht.containsKey(new AA(87653))); 49 //下面語句可以刪除最后一個key-value對 50 ht.remove(new AA(1232)); 51 for(Object key:ht.keySet()){ 52 System.out.print(key + "---->"); 53 System.out.print(ht.get(key) + "\n"); 54 } 55 } 56 }

  程序最后展示了如何遍歷Map中的全部key-value對:調用Map對象的keySet方法返回全部key組成的Set對象,通過遍歷Set的元素(就是Map的全部key)就可以遍歷Map中的所有鍵值對。

  與HashSet類似的是,盡量不要使用可變對象作為HashMap、Hashtable的key,如果確實需要使用可變對象作為HashMap、Hashtable的key,則盡量不要在程序中修改作為key的可變對象。

  HashSet有一個子類是LinkedHashSet,HashMap則有一個子類:LinkedHashMap;LinkedHashMap也使用雙向鏈表來維護key-value對的次序,該鏈表定義了迭代次序,迭代順序與插入順序時候保持一致。

  LinkedHashMap可以避免需要對HashMap、Hashtable里的key-value對進行排序(只要插入key-value對時保持順序即可)。同時可以避免使用TreeMap所增加的成本。LinkedHashMap需要維護元素的插入順序,因此性能略低于HashMap的性能,但在迭代訪問Map里的全部元素時將有很好的性能,因為它以鏈表來維護內部順序。下面程序示范了LinkedHashMap的功能:迭代輸出LinkedHashMap的元素時,將會按添加key-value對相同順序輸出。

Properties類???????????????????????????

  Properties類是Hashtable類的子類,該文件處理屬性文件(ini文件就是一種屬性文件)。Properties類可以把Map對象和屬性文件關聯起來,從而把Map對象中的key-value對寫入屬性文件,也可以把屬性文件中的屬性名=屬性值加載到Map對象中。由于屬性文件里的屬性名、屬性值只能是字符串類型,所以Properties里的key、value都是字符串類型,該類提供了如下三個方法來修改Properties里的key、value值。

方法:

  String getProperty(String key):獲取Properties中指定屬性名對應的屬性值,類似于Map的get(Object key)方法

  String getProperty(String key,String defaultValue):該方法與前一個方法基本類似,該方法多一個功能,如果Properties中不存在指定key時,該方法返回默認值

  Object setProperty(String key,String value):設置屬性值,類似Hashtable的put方法 除此之外,它還提供了兩個讀、寫屬性文件的方法

  void load(InputStream inStream):從屬性文件(以輸入流表示)中加載屬性名=屬性值,把加載到的屬性名=屬性值對追加到Properties里(由于Properties是Hashtable的子類,它不保證key-value對之間的次序)。

  void store(OutputStream out,String comments):將Properties中的key-value對寫入指定屬性文件(以輸出流表示)。

5.5?SortedMap和TreeMap

  正如Set接口派生出了SortedSet子接口,SortedSet接口有一個TreeSet實現類,Map接口也派生了一個SortedMap子接口,SortedMap也有一個TreeMap實現類。 與TreeSet類似的是,TreeMap也是基于紅黑樹對TreeMap中所有key進行排序,從而保證TreeMap中的鍵值對處于有序狀態。類似也有兩種排序方式。

排序方式

  自然排序:TreeMap的所有key必須實現Comparable接口,而且所有key應該是同一個類對象,否則拋出ClassCastException異常   定制排序:創建TreeMap時,傳入一個Comparator對象,該對象負責對TreeMap中所有key進行排序,采用定制排序時不要求Map的key實現Comparable接口   類似地,TreeSet中判斷兩個元素相等的標準,TreeMap中判斷兩個key相等的標準也是兩個key通過equals比較返回true,而通過compareTo方法返回0,TreeMap即認為這兩個key是相等的。   如果想使用自定義的類作為TreeMap的key,且想讓TreeMap工作良好,重寫該類的equals方法和compareTo方法時應該有一致的返回結果:即兩個key通過equals方法比較返回true時,通過compareTo方法返回0,否則處理會有性能問題,具體可以參考TreeSet。

TreeMap方法???

  與TreeSet類似的是,TreeMap提供了系列根據key順序訪問Map中key-value對方法。
  Map.Entry firstEntry():返回該Map中最小key所對應的鍵值對,如果該Map為空,則返回null
  Object firstKey():返回該Map中的最小key值,如果Map為空,則返回null
  Map.Entry lastEntry():返回該Map中最大key所對應的key-value對,如果該Map為空,或不存在這樣的key-value則返回null
  Object lastKey():返回該Map中的最大key值,如果Map為空,或不存在這樣的key都返回null
  Map.Entry higherEntry(Object key):返回該Map中key的上確界對應的鍵值對
  Object higherKey(Object key):返回key的上確界
  Map.Entry lowerEntry(Object key):返回該Map中key的下確界對應的鍵值對
  Object lowerKey(Object key):返回key的下確界
  NavigableMap subMap(Object fromKey,boolean fromInclusive,Object toKey,boolean toInclusive):返回該Map的子Map,其key的范圍從fromKey(是否包括取決于第二個參數)  到toKey(是否包括取決于第四個參數)
  NavigableMap tailMap(Object fromKey,boolean inclusive):返回該Map的子Map,其key的范圍從fromKey(是否包括取決于第二個參數)
  NavigableMap headMap(Object toKey,boolean inclusive):返回該Map的子Map,其key的范圍小于toKey(是否包括取決于第二個參數)
  SortedMap subMap(Object fromKey,Object toKey):返回該Map的子Map,其key的范圍從fromKey(包括)到toKey(不包括)
  SortedMap tailMap(Object fromKey):返回該Map的子Map,其key的范圍從fromKey(不包括)
  SortedMap headMap(Object toKey):返回該Map的子Map,其key的范圍小于toKey(不包括)

  上面的看起來方法挺多:也就是第一個、前一個、后一個、最后一個鍵值對方法,并提供了截取子TreeMap的方法

5.6?WeakHashMap實現類

  WeakHashMap與HashMap用法基本類似,但與HashMap區別在于,HashMap的key保留對實際對象的強引用,這意味著只要該HashMap對象不被銷毀,該HashMap對象所有key所引用的對象不會被垃圾回收,HashMap也不會自動刪除這些key所對應的key-value對象;但WeakHashMap的key只保留對實際對象的弱引用,這意味著如果該HashMap對象所有key所引用的對象沒有被其他強引用變量所引用,key所引用的對象可能被垃圾回收,HashMap有可能自動刪除這些key所對應的key-value對象   WeakHashMap中的每個key對象保存了實際對象的弱引用,因此,當垃圾回收了該key所對應的實際對象之后,WeakHashMap會自動刪除key-value對。

1 package chapter7; 2 3 import java.util.*; 4 5 public class TestWeakHashMap { 6 public static void main(String[] args){ 7 //添加一個集合,并添加匿名對象(無變量引用) 8 WeakHashMap whm = new WeakHashMap(); 9 whm.put(new String("English"), "80"); 10 whm.put(new String("Chemistry"), "80"); 11 whm.put(new String("Java"), "80"); 12 System.out.println(whm); 13 //添加一個有變量引用的對象 14 whm.put("Deutsch", "99"); 15 System.out.println("垃圾回收前:" + whm); 16 //通知系統進行垃圾回收 17 System.gc(); 18 //輸出垃圾回收后的結果 19 System.out.println("垃圾回收后:" + whm); 20 //對比HashMap垃圾處理機制 21 HashMap hm = new HashMap(); 22 hm.put(new String("English"), "80"); 23 hm.put(new String("Chemistry"), "80"); 24 hm.put(new String("Java"), "80"); 25 System.out.println(hm); 26 //添加一個有變量引用的對象 27 hm.put("Deutsch", "99"); 28 System.out.println("垃圾回收前:" + hm); 29 //通知系統進行垃圾回收 30 System.gc(); 31 //輸出垃圾回收后的結果 32 System.out.println("垃圾回收后:" + hm); 33 34 } 35 } 36 執行結果如下: 37 {Java=80, English=80, Chemistry=80} 38 垃圾回收前:{Java=80, English=80, Deutsch=99, Chemistry=80} 39 垃圾回收后:{Deutsch=99} 40 {Chemistry=80, Java=80, English=80} 41 垃圾回收前:{Chemistry=80, Deutsch=99, Java=80, English=80} 42 垃圾回收后:{Chemistry=80, Deutsch=99, Java=80, English=80}

5.7 IdentityHashMap實現類

  這個Map實現類的實現機制與HashMap基本相似,但它在處理兩個key相等時比較獨特:在IdentityHashMap中,當且僅當兩個key嚴格相等(key1==key2)時,IdentityHashMap才認為兩個key相等,對于普通HashMap而言,只要key1和key2通過equals方法比較返回true,且hashCode值相同即可。   IdentityHashMap是一個特殊的Map實現,它有意違反Map的通常規范:在IdentityHashMap中,當且僅當兩個key嚴格相等(key1==key2)時,IdentityHashMap才認為兩個key相等 IndetityHashMap提供了與HashMap基本類似的方法,null作為key和value,不保證鍵值對之間的順序。

1 package chapter7; 2 3 import java.util.*; 4 /** 5 * Description: 6 * <br/>Copyright (C), 2005-2008, Yeeku.H.Lee 7 * <br/>This program is protected by copyright laws. 8 * <br/>Program Name: 9 * <br/>Date: 10 * @author Yeeku.H.Lee kongyeeku@163.com 11 * @version 1.0 12 */ 13 public class TestIdentityHashMap{ 14 public static void main(String[] args){ 15 IdentityHashMap ihm = new IdentityHashMap(); 16 //下面兩行代碼將會向IdentityHashMap對象中添加2個key-value對 17 ihm.put(new String("語文") , 89); 18 ihm.put(new String("語文") , 78); 19 ihm.put("java" , 93); 20 ihm.put("java" , 98); 21 System.out.println(ihm); 22 } 23 }

注意:最后添加的兩個key為java的鍵值對是一樣的,因為java采用的緩存機制,對于同一個字符串,不新建新的對象來浪費內存

5.8?EnumMap集合類

  EnumMap是一個與枚舉類一起使用的Map實現,EnumMap中所有key都必須是單個枚舉類的枚舉值。創建EnumMap時必須顯式或隱式指定它對應的枚舉類。EnumMap在內部以數組形式保存,所以這種實現形式非常緊湊、高效。

  EnumMap根據key的定義時順序來維護鍵值對的次序,當使用keySet()、entrySet()、values()等方法來遍歷EnumMap時即可看到這種順序與插入時候順序是一致的。

  EnumMap不允許使用null昨晚key值,但允許使用null作為value,如果試圖使用null作為key將拋出NullPointerException異常,如果只是查詢是否包含值為null的key,或者刪除使用刪除值為null的key都不會拋出異常。

  與創建普通Map有區別的是,創建EnumMap時必須指定一個枚舉類,從將該EnumMap和指定枚舉類關聯起來。

  對于Map的常用實現類而言,HashMap和Hashtable的效率大致相同,因為實現機制幾乎完全一樣,但通常HashMap通常比Hashtable快一點,因為Hashtable額外實現同步操作。 TreeMap通常比HashMap、Hashtable要慢(尤其是插入、刪除鍵值對時候更慢),因為TreeMap需要額外的紅黑樹操作來維護key之間的次序,但是用TreeMap有一個好處:TreeMap中的鍵值對總是有序的,無序專門進行排序操作。當TreeMap被填充后,可以調用keySet(),取得key組成的Set,然后是用toArray()生成key的數組,接下來是用Arrays的binarySearch()方法在已排序的數組中快速地查詢對象。當然,通常只有在某些情況下無法使用HashMap的時候才這么做,因為HashMap正是為快速查詢而設計的,通常使用Map時候首選HashMap,除非需要一個總是排好序的Map時才使用TreeMap。       ?   LinkedHashMap比HashMap慢一點,因為它需要維護鏈表來保持Map中key的順序。IndentityHashMap性能沒有特別出色之處,EnumMap性能最好,但它只能使用同一個枚舉類的枚舉值作為key。

5.9 HashSet和HashMap的性能選擇

  對于HashSet及其子類而言,它們采用hash算法來決定集合中元素的存儲位置,并通過hash算法來增加集合容積的大小;對于HashMap、Hashtable及其子類而言,它們采用hash算法來決定Map中key的存儲,并通過hash算法來增加key Set容積的大小Hash表里可以存儲元素的位置被稱為"桶(bucket)",通常情況下,單個"桶"里存儲一個元素,此時有最好的性能:hash算法可以根據hashCode值計算出"桶"的存儲位置,接著從"桶"中取出元素,但hash表的狀態為open:當發送hash沖突時候,單個桶會存儲多個元素,這些元素以鏈表形式存儲,必須按順序搜索。如下圖

因為HashSet、HashMap、Hashtable都是用hash算法來決定其元素(對HashMap則是key)的存儲,因此HashSet、HashMap的hash表包含如下屬性:

Hash表屬性??

容量(capacity):hash表中桶的數量

初始化容量(initial capacity):創建hash表時桶的數量。HashMap和HashSet都允許在構造器中指定初始化容量

尺寸(size):當前hash表中記錄的數量

負載因子(load factor):負載因子等于size/capacity。負載因子為0,表示空的hash表,0.5 表示半滿的hash表,以此類推,輕負載的hash表具有沖突少、適宜插入與查詢的特點(但是使用Iterator迭代元素時候會變慢)

  除此之外,hash表里還有一個負載極限,負載極限在[0,1]的數值,負載極限決定了hash表中的最大填滿程度。當hash表中的負載因子到達指定的負載極限時,hash表會自動成倍地增加容量(桶的數量),并將原有的對象重新分配,放入新的桶內,也被稱為rehashing

  HashSet與HashMap、Hashtable的構造器允許指定一個負載極限,HashSet與HashMap、Hashtable默認的負載極限為0.75, 表明當該hash表被填到3/4 時,hash表會發生rehashing

  默認負載極限值0.75 是時間和空間成本上的一種折中:較高的負載極限可以降低hash表所占用的內存空間,但會增加查詢數據的時間開銷,而查詢是最頻繁的操作;較低的負載極限會增加查詢數據性能,但會增加hash表所占用的內存空間。通常無須改變HashSet和HashMap的負載極限值。通常不需要將初始化容量設置太高。

5.10?操作集合工具類Collections

  Java提供了一個操作Set、List和Map等集合的工具類:Collections,該工具類提供了大量方法對集合元素進行排序、查詢和修改等操作,還提供了將集合對象設置為不可變、對集合對象實現同步控制等方法。

5.10.1 排序操作

  Collections提供了如下幾個方法對List集合元素進行排序   static void reverse(List list):反轉指定List集合中元素的順序   static void shuffle(List list):對List集合元素進行隨機排序(shuffle方法模擬了洗牌動作)   static void sort(List list):根據元素的自然順序對指定List集合的元素按升序進行排序   static void sort(List list,Comparator c):根據指定Comparator產生的順序對List集合的元素進行排序   static void swap(List list,int i,int j):將指定List集合中i處元素和j處元素進行交換   static void rotate(List list,int distance):將指定集合中i處元素和list.length-i-1 處的元素進行交換

5.10.2查找,替換操作??????????????

  Collections還提供了如下用于查找、替換集合元素的常用方法   static int binarySearch(List list,Object key):使用二分搜索法搜索指定List集合,以獲得指定對象在List集合中的索引,如果要該方法可以正常工作,必須保證List中的元素已經處于有序狀態

  static Object max(Collection coll):根據元素的自然順序,返回給定集合中的最大元素   static Object max(Collection coll,Comparator comp):根據指定Comparator產生的順序,返回給定集合的最大元素   static Object min(Collection coll):根據元素的自然順序,返回給定集合中的最小元素   static Object min(Collection coll,Comparator comp):根據指定Comparator產生的順序,返回給定集合的最小元素   static void fill(List list,Object obj):使用指定元素obj替換指定List集合中的所有元素   static int frequency(Collection c,Object o):返回指定集合中等于指定對象的元素數量   static int indexOfSubList(List source,List target):返回子List對象在母List對象中第一次出現的位置索引;如果母List中沒有這樣的子List,則返回-1

  static int lastIndexOfSubList(List source,List target):返回子List對象在母List對象中最后一次出現的位置索引;如果母List中沒有這樣的子List,則返回-1   static boolean replaceAll(List list,Object oldVal,Object newVal):使用一個新值newVal替換List對象所有的舊值oldVal

5.10.3 同步控制???????????????????????

  Collections類中提供了多個synchronizedXxx方法(Xxx代表集合類名稱),該方法返回指定集合對象對應的同步對象,從而可以解決多線程并發訪問集合時的線程安全問題。 如前所述,Java常用的集合框架中推薦使用的三個實現類:HashSet、ArrayList和HashMap都是線程不安全的,如果有多條線程訪問它們,而且有超過一條線程試圖修改它們,則可能出現錯誤。Collections提供了多個靜態方法用于創建同步集合。

5.10.4 設置不可變集合?????????????

Collections提供了如下三類方法來返回一個不可變的集合,Xxx代表集合類名稱   emptyXxx():返回一個空的、不可變的集合對象,此處的集合可以是List、Set、Map   singleonXxx():返回一個只包含指定對象(只有一個或一項元素)的、不可變的集合對象,此處的集合可以是List、Set、Map   unmodifiableXxx:返回指定集合對象的不可變視圖。此處的集合可以是List、Set、Map 通過這樣的方法就可以創建"只讀"版本的集合,對象不可修改

5.10.5 煩瑣的:Enumeration

  Enumeration接口是Iterator迭代器的古老版本,目前都使用Iterator,保留Enumeration主要是為了照顧那些古老的程序。

6.小結????????????????????????????????????????????????????????????????????

  本部分詳細介紹了Java集合框架的相關知識。

  本部分從Java集合框架體系開始講起,概述了Java集合框架的三個主要體系:Set、List和Map,簡述集合在編程中的重要性。詳細講述了Set、Queue、List、Map接口及各實現類的詳細用法,深入分析了各實現類的機制差異,給出選擇集合實現類的原則,最后給出了Collections工具類的基本用法。

轉載于:https://www.cnblogs.com/people/archive/2013/05/08/3067086.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Java程序设计4——集合类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。