經常使用集合和方法

經常使用集合

sethtml

Set不按特定方式進行排序,而且沒有重複的對象,它的有些實現類能對集合中的對象按照特定的順序排序。主要有兩個實現類:HashSet和TreeSetjava

HashSet按照哈希算法來存取集合中的對象,存取速度比較快。程序員

TreeSet實現了SortSet接口,具備排序功能。算法

Listapi

List主要特徵是以線性方式存儲,集合中容許存放重複對象主要實現類包括ArrayList和LinkList數組

ArrayList表明長度可變的數組,容許對元素進行快速的隨機訪問,可是向ArrayList中插入與刪除元素較慢。安全

LinkList在實現中採用鏈表的結構,插入和刪除元素較快,隨機訪問則相對較慢。多線程

LinkList單獨具備addFirst(),addLast(),getFirst(),getLast(),removeFirst(),removeLast()等方法這些方法使得LinkList能夠做爲堆棧,隊列和雙向隊列使用併發

Queue框架

Queue隊列中的對象按照先進先出的規則來排列。在隊列末尾添加元素,在隊列的頭部刪除元素。能夠有重複對象。

雙向隊列Deque則容許在隊列的尾部和頭部添加和刪除元素。

由於LinkList能夠做爲雙向隊列使用,因此Queue和Deque使用比較少

Map

HashMap不保證映射的順序,特別是它不保證該順序恆久不變。(也就是無序)

TreeMapTreeMap基於紅黑樹(Red-Black tree)的 NavigableMap 實現,有序。

HashMap和TreeMap容許使用 null 值和 null 鍵 。

HashMap是使用hashCode和equals方法去重的。而TreeMap依靠Comparable或Comparator來實現key去重

併發類容器(集合)

  在java集合框架中,Set List Queue Map的實現類都沒有采起同步機制。在單線程環境中,這種實現方式會提升操縱集合的效率,java虛擬機沒必要會由於管理用不鎖而產生額外的開銷。

在多線程環境中,可能會有多個線程同時操縱一個集合,好比一個線程在爲集合排序,而另一個線程正不斷的向集合中添加新的元素。

爲了不併發問題,能夠直接採用java.util.concurrent併發包提供線程安全的集合。列如

  ConcurrentHashMap  、ConcurrentSkipListMapConcurrentSkipListSetConcurrentLinkedQueue

這些集合的底層實現採用了複雜的算法,保證多線程訪問集合時,既能保證線程之間的同步,又具備高效的併發性能。

  Hashtable:

    Hashtable,用做鍵的對象必須實現 hashCode 方法和 equals 方法。容器使用synchronized來保證線程安全,但在線程競爭激烈的狀況下Hashtable的效率很是低下。由於當一個線程訪問Hashtable的同步方法時,其餘線程訪問Hashtable的同步方法,就可能會進入阻塞或輪詢狀態。

    如線程1使用put進行添加元素,線程2不但不能使用put方法添加元素,而且也不能使用get方法來獲取元素,因此競爭越激烈效率越低。 

歷史集合類 :Vector Hashtable Stack Enumeration 在實現中都採用了同步機制,併發性能較差,所以不提倡使用它們。

  ConcurrentHashMap:

  儘管全部操做都是線程安全的,但獲取操做 必鎖定,而且 支持以某種防止全部訪問的方式鎖定整個表。

  容許經過可選的 concurrencyLevel 構造方法參數(默認值爲 16)來引導更新操做之間的併發,該參數用做內部調整大小的一個提示——摘自API

final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    //計算hash
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        //table是空的,進行初始化
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        //若是當前索引位置沒有值,直接建立
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            //cas 在 i 位置建立新的元素,當 i 位置是空時,即能建立成功,結束for自循,不然繼續自旋
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }
        //若是當前槽點是轉移節點,表示該槽點正在擴容,就會一直等待擴容完成
        //轉移節點的 hash 值是固定的,都是 MOVED
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        //槽點上有值的
        else {
            V oldVal = null;
            //鎖定當前槽點,其他線程不能操做,保證了安全
            synchronized (f) {
                //這裏再次判斷 i 索引位置的數據沒有被修改
                //binCount 被賦值的話,說明走到了修改表的過程裏面
                if (tabAt(tab, i) == f) {
                    //鏈表
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            //值有的話,直接返回
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            //把新增的元素賦值到鏈表的最後,退出自旋
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key,
                                                          value, null);
                                break;
                            }
                        }
                    }
                    //紅黑樹,這裏沒有使用 TreeNode,使用的是 TreeBin,TreeNode 只是紅黑樹的一個節點
                    //TreeBin 持有紅黑樹的引用,而且會對其加鎖,保證其操做的線程安全
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        //知足if的話,把老的值給oldVal
                        //在putTreeVal方法裏面,在給紅黑樹從新着色旋轉的時候
                        //會鎖住紅黑樹的根節點
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            //binCount不爲空,而且 oldVal 有值的狀況,說明已經新增成功了
            if (binCount != 0) {
                // 鏈表是否須要轉化成紅黑樹
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                //這一步幾乎走不到。槽點已經上鎖,只有在紅黑樹或者鏈表新增失敗的時候
                //纔會走到這裏,這二者新增都是自旋的,幾乎不會失敗
                break;
            }
        }
    }
    //check 容器是否須要擴容,若是須要去擴容,調用 transfer 方法去擴容
    //若是已經在擴容中了,check有無完成
    addCount(1L, binCount);
    return null;
}
View Code
  1. 若是數組爲空,初始化,初始化完成以後,走 2;
  2. 計算當前槽點有沒有值,沒有值的話,cas 建立,失敗繼續自旋(for 死循環),直到成功,槽點有值的話,走 3;
  3. 若是槽點是轉移節點(正在擴容),就會一直自旋等待擴容完成以後再新增,不是轉移節點走 4;
  4. 槽點有值的,先鎖定當前槽點,保證其他線程不能操做,若是是鏈表,新增值到鏈表的尾部,若是是紅黑樹,使用紅黑樹新增的方法新增;
  5. 新增完成以後 check 需不須要擴容,須要的話去擴容。

經常使用方法(Object類)

equals

  equals()方法判斷兩個對象是否「相等」。若是不重寫這個方法,equals兩個對象 除非是同一個引用,不然一直不相等(返回false)。

  Eclipse默認給咱們重寫的equals()方法,是對象的全部成員變量是否都相等,若是該對象和比較對象的成員變量都相等 則兩個對象互相equals() 也就是相等。

  咱們都知道Set集合存放的對象都是不可重複的,Set集合就是根據對象的equals方法判斷對象是否是重複

  注意:當此方法被重寫時,一般有必要重寫 hashCode 方法,以維護 hashCode 方法的常規協定,該協定聲明相等對象必須具備相等的哈希碼。

hashCode

  返回該對象的哈希碼值。支持此方法是爲了提升哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。

  若是根據 equals(Object) 方法,兩個對象是相等的,那麼對這兩個對象中的每一個對象調用 hashCode 方法都必須生成相同的整數結果。

  若是根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法 要求必定生成不一樣的整數結果。可是,程序員應該意識到,爲不相等的對象生成不一樣整數結果能夠提升哈希表的性能。

    ——摘自API,你們也就理解了 爲何eclipse中 equals和HasdCode方法給咱們的快捷鍵中,必須一塊兒重寫。

toString

   Object 類的 toString 方法返回一個字符串,該字符串由類名(對象是該類的一個實例)、at 標記符「@」和

        此對象哈希碼的無符號十六進制表示組成。換句話說,該方法返回一個字符串,它的值等於

   public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode()); }

以上是JDK默認toString方法的實現和API解釋,可見Object類默認的toString方法和equals方法都和對象的哈希碼有關

重寫toString方法可讓咱們更直觀的理解對象的內容屬性。

getClass

反射能夠拿到一個對象的成員變量的值,但方法的參數是出如今方法被調用的時候,
你拿到了class,method,參數類型等這些都沒用,這些都是靜態的,固定的。

也就是說反射拿不到參數的值

String StringBuffer和StringBuilder

StringBuffer表明可變的字符序列,往StringBuffer字符串加東西,直接相加。

String表明不可變字符序列  當咱們執行兩個字符串相加時 須要分配另一塊內存 而後 執行兩次copy。最後把字符串的指針執行新的那塊內存。

StringBuilder和StringBuffer相似 區別是StringBuilder線程非安全。

相關文章
相關標籤/搜索