1. 程式人生 > >Collection類之詳解(一)

Collection類之詳解(一)

Collection類之詳解(一)

一、概述

Collection 層次結構 中的根介面。

由來

由於陣列長度是固定,當新增的元素超過了陣列的長度時需要對陣列重新定義,所以java內部給我們提供了集合類,能儲存任意物件,長度是可以改變的,隨著元素的增加而增加,隨著元素的減少而減少。

陣列和集合的區別
區別1 : 
        陣列既可以儲存基本資料型別,又可以儲存引用資料型別,基本資料型別儲存的是值,引用資料型別儲存的是地址值
        集合只能儲存引用資料型別(物件)

,集合中也可以儲存基本資料型別,但是在儲存的時候會自動裝箱變成物件
區別2:
        陣列長度是固定的,不能自動增長
        集合的長度的是可變的,可以根據元素的增加而增長

以Collection的一個實現類ArrayList為例,其中當陣列長度不夠時,長度擴大程式碼如下:

//transient關鍵字的作用:讓某些被修飾的成員屬性變數不被序列化
//序列化:將物件轉換成以位元組序列的形式來表示,這些位元組序列包含了物件的資料和資訊,可被寫到資料庫或檔案中。
transient Object[] elementData;
private void grow(int minCapacity) {
        // overflow-conscious code
        //舊容量為當前陣列中元素個數
        int oldCapacity = elementData.length;
        //新容量=舊容量+舊容量的1/2,即3/2倍的舊容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //將原有陣列內容複製到新陣列的對應位置
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

陣列和集合什麼時候用
        如果元素個數是固定的推薦用陣列
        如果元素個數不是固定的推薦用集合

Collection層次結構圖

二、基本功能函式(以下均以ArrayList中實現為例)

 boolean add(E e)
          確保此 collection 包含指定的元素(可選操作)。
boolean remove(Object o)
          從此 collection 中移除指定元素的單個例項,如果存在的話(可選操作)。
 void clear()
          移除此 collection 中的所有元素(可選操作)。
 boolean contains(Object o)
          如果此 collection 包含指定的元素,則返回 true。
 boolean isEmpty()
          如果此 collection 不包含元素,則返回 true。
 int size()
          返回此 collection 中的元素數。

(1)public boolean add(E e)

transient Object[] elementData;
private int size;
public boolean add(E e) {
        //判斷是否超過陣列大小,如果超過,擴大陣列
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

注:add()方法如果是List集合一直都返回true,因為List集合中是可以儲存重複元素的 ;

如果是Set集合當儲存重複元素的時候,就會返回false;

在開發中一般忽略返回值。

(2)public boolean remove(Object o)

public boolean remove(Object o) {
        //存才對象o時,移除並返回true;否則返回false
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
//已從結構上修改 此列表的次數。從結構上修改是指更改列表的大小,或者打亂列表,從而使正在進行的迭代產生錯誤的結果。 
//此欄位由 iterator 和 listIterator 方法返回的迭代器和列表迭代器實現使用。
protected transient int modCount = 0;

//私有方法,僅可以被類中成員方法呼叫
private void fastRemove(int index) {
        modCount++;
        //需要移動的元素的個數,即刪除位置之後的元素個數
        int numMoved = size - index - 1;
        //將刪除位置之後的元素向前移動一個
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        //長度-1,最後一個值賦值為空,垃圾等待GC回收
        elementData[--size] = null; // clear to let GC do its work
    }

(3)public void clear()

public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

(4)public boolean contains(Object o)

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

public int indexOf(Object o) {
        //遍歷尋找,並返回所在位置索引值
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        //如果不存在,返回-1
        return -1;
    }

(5)public boolean isEmpty()

public boolean isEmpty() {
        return size == 0;
    }

(6)public int size()

public int size() {
        return size;
    }

注:ArrayList的父類的父類AbstractCollection重寫了toString()方法,呼叫時返回的是集合中內容。

三、集合的遍歷

把集合轉成陣列,可以實現集合的遍歷

Object[] arr = c.toArray();						//將集合轉換成陣列
for(int i = 0; i < arr.length; i++) {
	System.out.println(arr[i]);
}

注:toArray()返回的是Object[]陣列,如果需要用到子類具體方法,可以進行向下轉型。

Object[] arr = c.toArray();					//將集合轉換成陣列
for (int i = 0; i < arr.length; i++) {
	Student s = (Student)arr[i];			//向下轉型
	System.out.println(s.getName() + "..." + s.getAge());
}

四、帶All的功能函式

 boolean addAll(Collection<? extends E> c)
          將指定 collection 中的所有元素都新增到此 collection 中(可選操作)。
 boolean removeAll(Collection<?> c)
          移除此 collection 中那些也包含在指定 collection 中的所有元素(可選操作)。
 boolean containsAll(Collection<?> c)
          如果此 collection 包含指定 collection 中的所有元素,則返回 true。
 boolean retainAll(Collection<?> c)
          僅保留此 collection 中那些也包含在指定 collection 的元素(可選操作)。
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
//差集
public boolean removeAll(Collection<?> c) {
        //如果c為null,則丟擲異常
        Objects.requireNonNull(c);
        //如果c中不包含this中某元素則保留
        return batchRemove(c, false);
    }

//並集
public boolean retainAll(Collection<?> c) {
        //如果c為null,則丟擲異常
        Objects.requireNonNull(c);
        //如果c中包含this中某元素則保留
        return batchRemove(c, true);
    }

//Objects類中
public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }


private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        //開始設定為未修改
        boolean modified = false;
        try {
            for (; r < size; r++)
                //按需保留所需元素
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // 即使c.contains()丟擲異常,也要保持與AbstractCollection的行為相容性
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            //未丟擲異常,迴圈完成
            if (w != size) {
                // clear to let GC do its work
                //將不需要的元素賦值為空,等待垃圾回收機制回收
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                //調整已修改次數
                modCount += size - w;
                //調整大小
                size = w;
                //設定為已修改過
                modified = true;
            }
        }
        return modified;
    }
public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }

注:

c1.addAll(c2);							//將c2中的每一個元素新增到c1中
c1.add(c2);					    	        //將c2看成一個物件新增到c1中

五、迭代器遍歷

1.概述

集合是用來儲存元素,儲存的元素需要檢視,那麼就需要迭代(遍歷) 。

Iterator類:

對 collection 進行迭代的迭代器。迭代器取代了 Java Collections Framework 中的 Enumeration。迭代器與列舉有兩點不同:

  • 迭代器允許呼叫者利用定義良好的語義在迭代期間從迭代器所指向的 collection 移除元素。
  • 方法名稱得到了改進。

2.案例

Collection類:

 Iterator<E> iterator()
          返回在此 collection 的元素上進行迭代的迭代器。

 Iterator類:

 boolean hasNext()
          如果仍有元素可以迭代,則返回 true。
 E next()
          返回迭代的下一個元素。
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
while(it.hasNext()) {
	System.out.println(it.next());
}

 注:如果需要用子類方法,和陣列遍歷一樣需要向下轉型。

3.原理

迭代器是對集合進行遍歷,而每一個集合內部的儲存結構都是不同的,所以每一個集合存和取都是不一樣,那麼就需要在每一個類中定義hasNext()和next()方法,這樣做是可以的,但是會讓整個集合體系過於臃腫。

迭代器是將這樣的方法向上抽取出介面,然後在每個類的內部,定義自己迭代方式,這樣做的好處有二:

第一規定了整個集合體系的遍歷方式都是hasNext()和next()方法;

第二,程式碼有底層內部實現,使用者不用管怎麼實現的,會用即可;

ArrayList類中:

public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E>{
    //......
}