1. 程式人生 > >Java中的集合框架-Collection(一)

Java中的集合框架-Collection(一)

一,Collection介面

  在日常的開發工作中,我們經常使用陣列,但是陣列是有很多的侷限性的,比如:陣列大小固定後不可修改,只能儲存基本型別的值等等。

  基於陣列的這些侷限性,Java框架就產生了用於解決此類問題的工具,即集合框架。

  Java中有許多的集合框架類,基於這些類的共性特徵,向上高度抽取,便形成了共性的集合框架介面-Collection。

  由於此介面屬於工具性質的,所以它屬於util包,java核心的內容在lang核心包裡存放著。

  由於Collection是抽取了各集合的共性所形成的介面,所以所有的集合類都實現了此介面,此介面的特點如下:

    1,它是用於儲存物件的容器

    2,可動態擴充套件容量

    3,不能儲存基本型別的值

  我們在開發中對陣列或集合的操作無非是增刪改查四種操作,當然Collection介面既然是對各種集合的高度抽取的操作,它裡面的方法也無非這四種。下面對這四種方法進行分類總結

    1,新增

      add  將指定的某一個元素新增到集合中

      addAll  將指定的集合物件一次性新增到集合中

    2,修改

      對集合的修改無非是獲取集合中的某一個元素然後再重新賦給新值

    3,查詢(獲取)

      iterator  此方法返回一個迭代器物件,方便用於對集合中的每個元素進行迴圈獲取操作

      contains  是否包含某個元素

      containsAll  是否包含另一個集合中的所有元素

      equals  比較兩個集合是否相等

      isEmpty  判斷一個集合是否為空

      size  獲取集合中元素的數量

      retainAll  求和另外一個集合的交集

    4,刪除

      remove  刪除集合中的某一個元素

      removeAll  刪除集合中某一個子集(引數為集合物件)

      clear  清除集合中所有的元素

    5,其它

      toArray  返回包含此集合元素的陣列物件

      hashcode  返回此集合元素的雜湊碼

  以上是Collection介面的所有的方法,由於具體操作都由它的實現類來完成,並且都比較簡單,所以演示程式碼在介紹他們的實現類的時候再列出。

  Collection介面有著許多的實現類及子介面,這些實現類或子介面中,一些是允許重複的,另外一些不允許重複;一些是有序的,而另外一些則是無序的。日常開發中比較常用的大致分為兩類:List與Set(其實用了是這兩個介面的實現類),下面用類圖的方式從頂層往底層一層一層的新增Collection介面的子介面或實現類來把集合框架說完

                                

二,List介面

  1,List介面是有序的,這個有序是指儲存的到集合中的元素的順序與取出的順序是一致的;

1     private static void function_demo2() {
2         List list = new ArrayList();
3         list.add(0);
4         list.add(2);
5         list.add(3);
6         for (int i = 0; i < list.size(); i++) {
7             System.out.println(list.get(i));
8         }
9     }

   這段程式的執行結果:

            與新增元素的順序是一致的

 

  2,List接口裡的元素允許重複;同樣是上面的程式,我們再新增一個元素

          list.add(3);

    程式的執行結果如下:

              

 

  

  3,List接口裡的元素允許有null值;還是上面的程式,我們新增一個null元素

          list.add(null);

    程式執行結果如下:

            

  4,允許像陣列一樣用索引進行元素位置的操作;索引的位置從0開始,和陣列相類似

  5,List常用方法演示

 1     private static void function_demo1() {
 2         List list = new ArrayList();
 3         list.add("張三");// 將元素新增到列表的尾部
 4         list.add(0, "李四");// 將元素新增到列表的指定位置
 5         List list2 = new ArrayList();
 6         list2.add("王五");
 7         list2.add("趙六");
 8         list.addAll(list2);// 將一個集合中的元素新增到另一個集合中尾部
 9         list.addAll(0, list2);// 將一個集合中的元素新增到另一個集合中指定位置
10         for (int i = 0; i < list.size(); i++) {
11             System.out.println(list.get(i));// get方法獲取指定位置的元素
12         }
13         list.contains("張三");// 集合是否儲存了某一個元素,若儲存了該元素則返回true
14         list.containsAll(list2);// 集合是否包含了另外一個集合的所有元素,若包含了,則返回true
15         list.indexOf("張三");// 集合是查詢到第一次出現“張三”的索引位置,若集合中沒有“張三”則返回-1
16         list.lastIndexOf("張三");// 集合中最後一次出現“張三”的索引位置,若沒有“張三”則返回-1
17         list.isEmpty();// 集合是否沒有任何元素,若是空集合則返回true
18         list.remove("張三");// 移除第一次出現的“張三”
19         list.remove(0);// 移除指定位置上的元素
20         list.removeAll(list2);// 移除所包含的集合元素(差集)
21         list.retainAll(list2);// 兩個集合的並集
22         list.set(0, "馬六");// 設定指定位置的值
23         list.size();// 集合的元素個數
24         list.subList(2, 4);// 返回list的子集,包括位置2但不包括4
25         Object[] obj = list.toArray();// 返回集合中所有的元素組成的陣列
26         String[] ary = new String[list.size()];
27         list.toArray(ary);// 返回集合中所有的元素組成的陣列,但是可以指定其返回的陣列資料型別,即此方法的引數陣列型別
28         list.clear();// 移除集合中所有的元素
29     }

   注:以上程式碼中用for迴圈取出集合中的元素方法是List集合所物有的方式

  6,List裡的iterator和listIterator方法

   我們知道,用iterator方法可以獲取一個迭代器物件,然後方便對集合中的元素進行迴圈的訪問,現在有一個這樣的需求,迴圈遍歷集合中的元素,當元素為“張三”時,為集合新增一個元素,實現程式碼如下

 1     private static void function_demo3() {
 2         List list = new ArrayList();
 3         list.add("張三");// 將元素新增到列表的尾部
 4         list.add(0, "李四");// 將元素新增到列表的指定位置
 5         List list2 = new ArrayList();
 6         list2.add("王五");
 7         list2.add("趙六");
 8         list.addAll(list2);// 將一個集合中的元素新增到另一個集合中尾部
 9         list.addAll(0, list2);// 將一個集合中的元素新增到另一個集合中指定位置
10         Iterator it = list.iterator();//獲取迭代器物件
11         while (it.hasNext()) {
12             String str = it.next().toString();
13             if (str.equals("張三")) {
14                 list.add("馬七");
15             } else {
16                 System.out.println(str);
17             }
18         }
19     }

    程式執行結果:

      

  經除錯發現,程式在執行到“張三”這個元素後為集合新增元素時,丟擲了java.util.ConcurrentModificationException異常,此異常是當方法檢測到物件的併發修改,但不允許這種修改時,丟擲此異常。我們可以得出結果,就是iterator介面不允許我們在讀取的時候進行修改操作。

  而listIterator方法返回一個ListIterator介面,此介面允許在進行迭代的時候進行新增刪除修改的操作,並可按任一方向(前或後)進行遍歷操作,演示程式碼如下

 1     private static void function_demo3() {
 2         List list = new ArrayList();
 3         list.add("張三");// 將元素新增到列表的尾部
 4         list.add(0, "李四");// 將元素新增到列表的指定位置
 5         List list2 = new ArrayList();
 6         list2.add("王五");
 7         list2.add("趙六");
 8         list.addAll(list2);// 將一個集合中的元素新增到另一個集合中尾部
 9         list.addAll(0, list2);// 將一個集合中的元素新增到另一個集合中指定位置
10         ListIterator it = list.listIterator();// 獲取迭代器物件
11         while (it.hasNext()) {
12             String str = it.next().toString();
13             if (str.equals("張三")) {
14                 System.out.println(str);
15                 it.add("馬七");//此處用的是ListIterator的物件it即呼叫的是ListIterator的方法進行新增操作
16             } else {
17                 System.out.println(str);
18             }
19         }
20         System.out.println(list.size());
21     }

      使用listIterator不但可以在迭代的同時進行增刪改的操作,還可以回退著讀即呼叫hasPrevious方法,此處不再演示

      注:此功能僅有List集合具備

 三,List常用的實現類   Collection是集合框架的頂層介面,而List是Collection的子介面,我們開發中真正用到的是具體類裡面的方法,所以介紹一下比較常用的List的實現方法還是很有必要的。   List比較常用的方法有三個Vector,ArrayList,LinkedList,那麼接下來我們先接著上面的那個類圖來把這三個類新增上去,圖例如下:           

  1,List常用實現類Vector

    Vector類是個比較古老的類,從1.0版本便有了,也就是說此類是先於集合框架出現的,只是由於效率問題到後來併到List下面的。

  Vector類內部維護的是一個數組,此陣列可動態擴充套件。若例項化該類時用的是無參構造則陣列預設大小為10,當此容器內部的數量大於10時,容量則動態擴充套件一倍;也可在例項化該類時指定初始大小,並可指定增長量;演示如下:

 1     private static void function_demo4() {
 2         Vector v = new Vector();// 使用預設大小
 3         System.out.println("Vector預設容量:" + v.capacity());
 4         Vector v2 = new Vector(2);// 指定初始大小為2
 5         System.out.println("Vector預設容量:" + v2.capacity());
 6         v2.add(2);
 7         v2.add(3);
 8         System.out.println("元素個數:" + v2.size());
 9         System.out.println("Vector預設容量:" + v2.capacity());
10         v.add(3);
11         System.out.println("元素個數:" + v2.size());
12         System.out.println("Vector預設容量:" + v2.capacity());//此時元素個數超出來初始大小2,便動態擴充套件一倍的容量
13     }

     指定容量大小,並指定增長量大小演示如下:

 1     private static void function_demo5() {
 2         Vector v = new Vector(2, 1);// 預設大小為2,當超出後每次增長1
 3         v.add(2);
 4         v.add(3);
 5         System.out.println("元素個數:" + v.size());
 6         System.out.println("Vector預設容量:" + v.capacity());
 7         v.add(4);
 8         System.out.println("元素個數:" + v.size());
 9         System.out.println("Vector預設容量:" + v.capacity());
10     }

   Vector除了實現了List介面後重寫了List裡的方法,還維護著它自己之前的方法,這些方法不但名字較長而且效率低下,完成的功能與List裡的方法功能相同,如addElement方法和add方法,elementAt與indexOf方法等等,其中有一個方法用於遍歷容器中的元素elements返回一個列舉器物件Enumeration被iterator所替代。

2,List常用實現類LinkedList

LinkedList是List介面的連結串列列表體現,即它的內部是連結串列的資料結構,所以它增刪時的速度比較快,另外此類是非執行緒安全的,即非同步。

LinkedList除了實現了List介面的方法外,還提供了一些方法,這些方法可模擬堆疊,佇列,和雙端佇列的操作。

基本的增刪改查的方法就不再演示了,我們用LinkedList所提供的可模擬堆疊,佇列和雙端佇列的方法來完成一個自定義的堆疊及佇列操作。

我們知道,堆疊有一個特點即後進先出而佇列的特點是先進先出,基於此可有以下操作:

 1 public class DuiZhan {
 2     private LinkedList linkedList;
 3 
 4     public DuiZhan() {
 5         linkedList = new LinkedList();
 6     }
 7 
 8     public void add(Object e) {
 9         linkedList.push(e);
10     }
11 
12     public Object get() {
13         return linkedList.pollLast();
14     }
15 
16 }
 1 public class DuiLie {
 2     private LinkedList linkedList;
 3 
 4     public DuiLie() {
 5         linkedList = new LinkedList();
 6     }
 7 
 8     public void add(Object obj) {
 9         linkedList.push(obj);
10     }
11 
12     public Object get() {
13         return linkedList.pollFirst();
14     }
15 }

  3,List常用實現類ArrayList

  ArrayList類是開發中最常用的類,此類內部是陣列的形式;功能上基本與Vector相類似,但它是不同步的,是Vector的替代品;由於內部是陣列結構,所以用於查詢速度相對較快。

  ArrayList低層所維護的陣列的長度動態擴充套件大約每次擴充套件原來容量1.5倍的容量,但是ArrayList有一個方法ensureCapacity,此方法接收一個int型別的引數,這個方法的作用是當此方法所傳遞的引數為大於ArrayList預設容量的1.5倍時,ArrayList擴充套件為此引數大小的容量;如果此方法所傳遞的引數為小於ArrayList預設容量的1.5倍時,ArrayList擴充套件為預設值的1.5倍容量;這個容量的擴充套件與否關係著ArrayList執行效率的快慢問題,因為ArrayList擴容是新建一個數組,然後把原來陣列中的內容複製到新陣列中去,是很消耗資源的。下面用一個例子作以演示:

 1     private static void function_demo8() {
 2         int n = 100000;
 3         ArrayList al = new ArrayList();
 4         long startTime = System.currentTimeMillis();
 5         for (int i = 0; i < n; i++) {
 6             al.add(i);
 7         }
 8         System.out.print("預設擴充套件容量耗時:");
 9         System.out.println(System.currentTimeMillis() - startTime);
10         ArrayList al1 = new ArrayList();
11         al1.ensureCapacity(n);
12         startTime = System.currentTimeMillis();
13         for (int i = 0; i < n; i++) {
14             al1.add(i);
15         }
16         System.out.print("手動擴充套件容量耗時:");
17         System.out.println(System.currentTimeMillis() - startTime);
18     }

   程式執行結果:

        

 可以感受一下,這個差距還是很大的。另外ArrayList還有一個方法trimToSize,這個方法的作用是將容器的大小調整為與元素數量相同的大小,舉例說明一下:假設ArrayList預設容量為10,當新增第11個元素的時候容量會擴充套件成15,但此時元素只有11個,另外的四個位置其實是浪費的,那麼這個時候呼叫一下trimToSize方法會將多餘的那四個位置給清除掉,具體不再演示,需要去扒原始碼。ArrayList的其它方法基本上沒有什麼難理解的了,就不再一一演示。