1. 程式人生 > >重學java---5---集合

重學java---5---集合

Iterator

有三個方法:

  1. boolean hasNext();
  2. E next();
  3. void remove();
  • 很多具體集合類都提供了自己的迭代器,通過內部類來實現
  • 使用迭代器進行刪除時的正確方法是
    1. iterator.next();
    2. iterator.remove();
    3. 呼叫remove之間沒有呼叫next是不合法的

list

常用的有 ArrayList 和 LinkedList。

LinkedList在頻繁的中間插入操作場景中比ArrayList有更好的效能。並且不用擴容。

ArrayList在兩方面的效能比LinkedList要好:

  1. 隨機訪問與遍歷
  2. 一直從尾部新增元素

Arrays.asList(T…t);可以將一個可變長引數變為一個list,但這個list只是一個檢視,只能讀不能新增與刪除元素。

map中的 keySet()返回的也是一個set檢視。

雜湊

對於散列表而言,有兩個引數 和 兩個方法 比軟重要。

兩個引數:

  1. hash值
  2. 雜湊因子

兩個方法:

  1. hashCode()
  2. equels()

hash演算法的好壞直接影響到了元素的分佈狀況,hash演算法不合適會使元素分佈集中。

它的原理是這樣的:

  1. 散列表的結構應該是這樣: ArrayList hashSet,
    即第一層為陣列,然後每個陣列的元素又是一個連結串列。
  2. 新增第一個元素時,其hash值與陣列長度取模的值即為其存放的陣列下標。當然放於該下標下的Linkedlist的第一個元素。
  3. 當新增元素時,如果發生hash衝突,即陣列下標的LinkedList中已存在值,則通過equels方法決定元素是否在LinkedList的尾部新增。hashSet:相同則不新增。hashMap:相同則將原有的替換。

經過檢視HashMap程式碼,證明與猜想大致一樣。HashMap 的主要核心為 :Node<K, V>[ ] tab; 注意 tab 這是一個數組,陣列的元素為 Node<K, V>, Node 的一個節點並持有下一節點的引用(就是說它就是一個連結串列)
本以為 HashSet 也會像 HashMap 一樣來搞,檢視原始碼之後也是眼前一亮了:HashSet 的核心竟然直接就是 HashMap。它將的 map 的V 都設定成一個Object物件,然後利用map的K不能重複的特性達到set的目的,也就是說:map中的K的集合就是 set 的最終結果。

  1. TreeSet
  2. Treemap

兩個都是通過紅黑樹實現的。與散表不同的是:

  1. 這兩個再新增時就將元素排序好了
  2. 沒有雜湊新增和訪問快

佇列

  1. ArrayDeque
  2. LinkedList
  3. ProrityQueue

1, 2 都是雙端佇列,是Deque的具體實現類,必要時可以增加長度
3 是優先順序佇列,通過堆排序來實現的。在使用時,要麼元素類實現Comparator介面,要麼通過ProrityQueue的構造方法,傳遞一個Comparator物件。

檢視

檢視是對介面的包裝,只能呼叫介面中的方法。

  1. 不可修改社圖
    • Set Collections.unmodifiableSet(Set s); //類似還有list, map 等
    • 對檢視的修改會丟擲異常
  2. 同步檢視
    • List synchromizedList(List s);
    • 多執行緒訪問list檢視時,用於修改的方法都是同步的
  3. 檢查檢視
    • <K, V> Map checkedMap(Map<K, V> m);
    • 如果新增一個錯誤的型別元素時,丟擲異常

集合取交集

Set result = new hashSet(a);
result.retainAll(b);
a, b 為Collection子類。

集合變陣列

集合的 toArray()方法返回的是Object陣列。
正確處理泛型的方法如下:
Type[] typs = 集合物件.toArray(new Type[0]);

Collections 提供了不少有用的操作集合的方法。

集合的執行緒同步正確做法

    // 普通的集合類
    List<Integer> list = new ArrayList();
    // 同步集合檢視
    List<Integer> synchronizedList = Collections.synchronizedList(list);

    /*
        1. 建立5個執行緒,並啟動;每個執行緒向lis中新增100個數
        2. 執行緒不同步 test1 直接丟擲了 ArrayIndexOutOfBoundsException 異常
        3. 執行緒同步 test2 可以列印正確結果;並且 test2 中的執行緒操作的都是 synchronizedList,但是最終其實是把資料新增到了 list 中
        所以最後 list 的長度是 500.
    */

    @Test
    public void test1() throws InterruptedException {

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    list.add(j);
                }
            }).start();
        }

        Thread.sleep(5000);
        System.out.println(list.size());

    }

    @Test
    public void test2() throws InterruptedException {
        
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    synchronizedList.add(j);
                }
            }).start();
        }
        
        Thread.sleep(5000);
        System.out.println(synchronizedList.size());
        System.out.println(list.size());
    }