數學知識巧學JCF(Java Collections framework)

 

 不知你是否還記得高中咱們學過的集合,映射,函數,數學確實很牛逼,拿它來研究java集合類,垂手可得的就把知識理解了。本篇文章適合初學java集合類的小白,也適合補充知識漏缺的學習者,同時也是面試者能夠參考的一份資料。java

數學知識

回顧一下以前所學的知識,結合我多年的高中數學教學經驗,相信你會對某些知識有一些新的感悟。node

集合:通常地,咱們把研究對象統稱爲元素(element),把一些元素組成的整體叫作集合(set)。面試

對於一個給定的集合,其具備的特徵:數組

肯定性:集合中的元素都是肯定的。微信

互異性:集合中的元素都是不一樣的。數據結構

無序性:集合中的元素的順序是無序的。多線程

映射:通常地,咱們有:app

設A,B是兩個非空的集合,若是按照某一個肯定的對應關係f.是對應集合A中的任意一個元素x,在集合B中都有惟一肯定的元素y與之對應,那麼就稱對應f:A—>B爲集合A到集合B的一個映射(mapping)。框架

其實簡單的來說,何謂映射,就是函數上將的關係對應,例如:ide

函數 f(x)=x^2  那麼每個x都有惟一的y與之對應,這就是映射關係的一個模型。

而方程 x^2+y^2=1,這個很明顯是圓心爲(0,0)的半徑爲1的圓,任取一個x可能會有一個或者兩個y與之對應,這就不能稱爲映射,進而不能稱爲函數。(1,0)或者(-1,0)這時候的x只有惟一的肯定的y和它對應。

集合類的學習

集合類產生的緣由:在通常的狀況下,咱們在寫程序時並不知道將須要多少個對象,或者是否須要更加複雜的方式存儲對象,顯然使用具備固定長度的數組已經不能解決這個問題了。因此java 實用類庫提供了一套至關完整的容器類來解決這個問題。

基本概念

java容器類類庫的用途是「保存對象」,可將其劃分爲兩個不一樣的概念:

1)collection.獨立元素的序列。主要包含List(序列),Set(集合),Queue(隊列)

List:按照插入的順序保存元素;

Set:不能有重複的元素;

Queue:按照排隊規則來肯定對象產生的順序(一般與它們被插入的順序相同);

2)Map:一組成對的「鍵值對」對象,容許咱們使用鍵來查找值。

針對常用的類庫,咱們只列出List,Set,Map之間的繼承關係:

List

List接口在Collection的基礎上添加了大量的方法,使得能夠在List的中間插入和刪除元素。

繼承自List的子類有ArrayList,   LinkedList ,Vector三類。

list的特徵:

1 有序的Collection
2 容許重複的元素,容許空的元素。
3 插入相似的數據:{1,2,4,{5,2},1,3};

ArrayList(相似於順序表)

其主要用於查找,對於刪除和插入,耗時巨大。ArrayList是以數組實現的列表,不支持同步。

優勢:利用索引位置能夠快速的定位訪問

   適合變更不大,主要用於查詢的數據

         和java的數組相比較,其容量是能夠動態調整的。

     缺點:不適合指定位置的插入,刪除操做。

--ArrayList在元素填滿容器是會自動擴充容器大小的50%

ArrayListTest 代碼分析:

add()方法,添加元素,默認是在後面添加。

add(index,value),在指定索引處添加元素。會進行元素的移動。源碼以下:

1 public void add(int index, E element) {
2         rangeCheckForAdd(index);
3         ensureCapacityInternal(size + 1);  // Increments modCount!!
4         System.arraycopy(elementData, index, elementData, index + 1,
5                          size - index);
6         elementData[index] = element;
7         size++;
8  }

remove(index)刪除指定位置上的元素。源碼以下:

 1 public E remove(int index) {
 2         rangeCheck(index);
 3         modCount++;
 4         E oldValue = elementData(index);
 5         int numMoved = size - index - 1;
 6         if (numMoved > 0)
 7             System.arraycopy(elementData, index+1, elementData, index,
 8                              numMoved);
 9         elementData[--size] = null; // clear to let GC do its work
10         return oldValue;
11     }

  從源碼能夠分析出,在ArrayList進行插入和刪除的時候,會進行相似順序表的操做,移動元素,空出位置,而後插入元素。刪除:依次移動後面的元素覆蓋指定位置的元素。這就會大大減慢ArrayList插入和刪除的效率。

舉一個應用的例子,更好的理解ArrayList:

 1 public class ArrayListTest {
 2   public static void main(String[] args) {
 3      //泛型的用法,只容許Integer類型的元素插入。
 4     ArrayList<Integer> arrayList =new ArrayList<Integer>();
 5   //增長元素 
 6            arrayList.add(2);
 7     arrayList.add(3);
 8     arrayList.add(4);
 9     arrayList.add(5);
10     arrayList.add(4);
11     arrayList.add(null);//ArrayList容許空值插入,
12     arrayList.add(new Integer(3));
13     System.out.println(arrayList);//   [2, 3, 4, 5, 4, null, 3]
14   //查看元素的個數
15            System.out.println(arrayList.size());// 7
16     arrayList.remove(0);
17     System.out.println(arrayList);//  [3, 4, 5, 4, null, 3]
18     arrayList.add(1, new Integer(9)); 
19     System.out.println(arrayList);//   [3, 9, 4, 5, 4, null, 3]
20     System.out.println("-----------遍歷方法-------");
21     ArrayList<Integer> as=new ArrayList<Integer>(100000);
22     for(int i=0;i<100000;i++){
23       as.add(i);
24     }
25     traverseByIterator(as);
26     traverseByFor(as);
27     traverseByForEach(as);
28   }
29   public static void traverseByIterator(ArrayList<Integer>al){
30     System.out.println("---------迭代器遍歷-------------");
31     long startTime=System.nanoTime();//開始時間
32     Iterator it=al.iterator();
33     while(it.hasNext()){//
34       it.next();
35     }
36     long endTime=System.nanoTime();//結束時間
37     System.out.println((endTime-startTime)+"納秒");
38   }
39   public static void traverseByFor(ArrayList<Integer>al){
40     System.out.println("---------索引遍歷-------------");
41     long startTime=System.nanoTime();//開始時間
42     for(int i=0;i<al.size();i++) al.get(i);
43     long endTime=System.nanoTime();//結束時間
44     System.out.println((endTime-startTime)+"納秒");
45   }
46   public static void traverseByForEach(ArrayList<Integer>al){
47     System.out.println("---------Foreach遍歷-------------");
48     long startTime=System.nanoTime();//開始時間
49     for(Integer temp:al);
50     long endTime=System.nanoTime();//結束時間
51     System.out.println((endTime-startTime)+"納秒");
52   }
53 }
54 -----------遍歷方法-------
55 ---------迭代器遍歷-------------
56 10407039納秒
57 ---------索引遍歷-------------
58 7094470納秒
59 ---------Foreach遍歷-------------
60 9063813納秒
61 能夠看到利用索引遍歷,相對來講是快一些。
View Code

迭代器 Iterator

1 hasNext()  判斷是否有下一個元素
2 next()    獲取下一個元素
3 remove ()  刪除某個元素

LinkedList:(主要用於增長和修改!)

    --以雙向鏈表實現的列表,不支持同步。

    --能夠被當作堆棧、隊列和雙端隊列進行操做

    --順序訪問高效,隨機訪問較差,中間插入和刪除高效

    --適合常常變化的數據

    addFirst()在頭部添加元素

    add(3,10);將10插入到第四個位置上

    remove(3)刪除第四個位置的元素

代碼詳解:

 1 public class LinkedListTest {
 2   public static void main(String[] args) {
 3     LinkedList<Integer> linkedList=new LinkedList<Integer>();
 4     linkedList.add(2);
 5     linkedList.add(3);
 6     linkedList.add(9);
 7     linkedList.add(6);
 8     linkedList.add(7);
 9     System.out.println(linkedList);
10     //linkedList.addFirst(1);
11     //linkedList.addLast(10);
12     //System.out.println(linkedList);
13     linkedList.add(3, 4);
14     System.out.println(linkedList);
15     System.out.println(linkedList.get(4));
16     LinkedList<Integer> as=new LinkedList<Integer>();
17     for(int i=0;i<100000;i++){
18       as.add(i);
19     }
20     traverseByIterator(as);
21     traverseByFor(as);
22     traverseByForEach(as);
23   }
24   public static void traverseByIterator(LinkedList<Integer>al){
25     System.out.println("---------迭代器遍歷-------------");
26     long startTime=System.nanoTime();//開始時間
27     Iterator it=al.iterator();
28     while(it.hasNext()){
29       it.next();
30     }
31     long endTime=System.nanoTime();//結束時間
32     System.out.println((endTime-startTime)+"納秒");
33   }
34   public static void traverseByFor(LinkedList<Integer>al){
35     System.out.println("---------索引遍歷-------------");
36     long startTime=System.nanoTime();//開始時間
37     for(int i=0;i<al.size();i++) al.get(i);
38     long endTime=System.nanoTime();//結束時間
39     System.out.println((endTime-startTime)+"納秒");
40   }
41   public static void traverseByForEach(LinkedList<Integer>al){
42     System.out.println("---------Foreach遍歷-------------");
43     long startTime=System.nanoTime();//開始時間
44     for(Integer temp:al);
45     long endTime=System.nanoTime();//結束時間
46     System.out.println((endTime-startTime)+"納秒");
47   }
48 }
49 ---------迭代器遍歷-------------
50 6562423納秒
51 ---------索引遍歷-------------
52 4565606240納秒
53 ---------Foreach遍歷-------------
54 4594622納秒
55 能夠看出使用索引遍歷,對於linkedList真的很費時間!

add(index,value)源碼分析:咱們能夠看到,這就是雙引用(雙指針)的賦值操做。

 1 void linkBefore(E e, Node<E> succ) {
 2         // assert succ != null;
 3         final Node<E> pred = succ.prev;
 4         final Node<E> newNode = new Node<>(pred, e, succ);
 5         succ.prev = newNode;
 6         if (pred == null)
 7             first = newNode;
 8         else
 9             pred.next = newNode;
10         size++;
11         modCount++;
12     }

remove(index)源碼分析:一樣,這也是對引用的更改操做,方面多了!

 1 E unlink(Node<E> x) {
 2         // assert x != null;
 3         final E element = x.item;
 4         final Node<E> next = x.next;
 5         final Node<E> prev = x.prev;
 6 
 7         if (prev == null) {
 8             first = next;
 9         } else {
10             prev.next = next;
11             x.prev = null;
12         }
13 
14         if (next == null) {
15             last = prev;
16         } else {
17             next.prev = prev;
18             x.next = null;
19         }
20 
21         x.item = null;
22         size--;
23         modCount++;
24         return element;
25     }

get(index)源碼分析:利用指針挨個日後查找,直到找到位置爲index的元素。固然了,找的時候也是要注意方法的,好比說利用二分查找。

 1 Node<E> node(int index) {
 2         // assert isElementIndex(index);
 3 
 4         if (index < (size >> 1)) {
 5             Node<E> x = first;
 6             for (int i = 0; i < index; i++)
 7                 x = x.next;
 8             return x;
 9         } else {
10             Node<E> x = last;
11             for (int i = size - 1; i > index; i--)
12                 x = x.prev;
13             return x;
14         }
15     }

Vector

-和ArrayList相似,可變數組實現的列表

    -Vector同步,適合在多線程下使用

    -原先不屬於JCF框架,屬於java最先的數據結構,性能較差

    -從JDK1.2開始,Vector被重寫,並歸入JCF中

    -官方文檔建議在非同步的狀況下,優先採用ArrayList

    其實vector相似於ArrayList,因此在通常狀況下,咱們能優先使用ArrayList,在同步的狀況下,是能夠考慮使用Vector

代碼例子:

 1 public class VectorTest {
 2   public static void main(String[] args) {
 3     Vector<Integer> vs=new Vector<Integer>();
 4     vs.add(1);
 5     vs.add(4);
 6     vs.add(3);
 7     vs.add(5);
 8     vs.add(2);
 9     vs.add(6);
10     vs.add(9);
11     System.out.println(vs);
12     System.out.println(vs.get(0));
13     vs.remove(5);
14     System.out.println(vs);
15     /*Integer []a=new Integer[vs.size()];
16     vs.toArray(a);
17     for(Integer m:a){
18       System.out.print(m+" ");
19     }*/
20     Vector <Integer> as=new Vector <Integer>(100000);
21     for(int i=0;i<1000000;i++){
22       as.add(i);
23     }
24     traverseByIterator(as);
25     traverseByFor(as);
26     traverseByForEach(as);
27     traverseEm(as);
28   }
29   public static void traverseByIterator(Vector<Integer>al){
30     System.out.println("---------迭代器遍歷-------------");
31     long startTime=System.nanoTime();//開始時間
32     Iterator it=al.iterator();
33     while(it.hasNext()){
34       it.next();
35     }
36     long endTime=System.nanoTime();//結束時間
37     System.out.println((endTime-startTime)+"納秒");
38   }
39   public static void traverseByFor(Vector<Integer>al){
40     System.out.println("---------索引遍歷-------------");
41     long startTime=System.nanoTime();//開始時間
42     for(int i=0;i<al.size();i++) al.get(i);
43     long endTime=System.nanoTime();//結束時間
44     System.out.println((endTime-startTime)+"納秒");
45   }
46   public static void traverseByForEach(Vector<Integer>al){
47     System.out.println("---------Foreach遍歷-------------");
48     long startTime=System.nanoTime();//開始時間
49     for(Integer temp:al){
50       temp.intValue();
51     }
52     long endTime=System.nanoTime();//結束時間
53     System.out.println((endTime-startTime)+"納秒");
54   }
55   public static void traverseEm(Vector<Integer>al){
56     System.out.println("---------Enumeration遍歷-------------");
57     long startTime=System.nanoTime();//開始時間
58     for(Enumeration <Integer> ei=al.elements();ei.hasMoreElements();){
59       ei.nextElement();
60     }
61     long endTime=System.nanoTime();//結束時間
62     System.out.println((endTime-startTime)+"納秒");
63   }
64 }
65 ---------迭代器遍歷-------------
66 28927404納秒
67 ---------索引遍歷-------------
68 32122768納秒
69 ---------Foreach遍歷-------------
70 25191768納秒
71 ---------Enumeration遍歷-------------
72 26901515納秒
73 能夠看到Foreach遍歷要快於其餘的遍歷方法。

add(index,value)源碼剖析:這個和ArrayList相似,須要進行元素的複製,因此很慢

 1 public synchronized void insertElementAt(E obj, int index) {
 2         modCount++;
 3         if (index > elementCount) {
 4             throw new ArrayIndexOutOfBoundsException(index
 5                                                      + " > " + elementCount);
 6         }
 7         ensureCapacityHelper(elementCount + 1);
 8         System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
 9         elementData[index] = obj;
10         elementCount++;
11     }

get(index)源碼剖析:能夠看到,直接根據元素的下表返回數組元素。很是快!

1 public synchronized E get(int index) {
2         if (index >= elementCount)
3             throw new ArrayIndexOutOfBoundsException(index);
4 
5         return elementData(index);
6     }
7 E elementData(int index) {
8         return (E) elementData[index];
9     }

其實List這部份內容用的數學知識不是不少,可是set和Map確實是相似於數學模型的概念。期待後續Set,Map的學習。

我的微信公衆號

相關文章
相關標籤/搜索