天天看點

java學習筆記(11)——集合詳解java學習筆記(11)——集合

java學習筆記(11)——集合

1 概述

數組和集合優缺點?
  • 相同點:集合、數組都是對多個資料進行存儲操作的結構,簡稱Java容器
  • 數組弊端:

    數組一旦定義好,其元素的類型也就确定了

    一旦初始化以後,其長度就不可修改。

    數組中提供的方法非常限,對于添加、删除、插入資料等操作,非常不便,同時效率不高。

    擷取數組中實際元素的個數的需求,數組沒有現成的屬性或方法可用

    數組存儲資料的特點:有序、可重複。對于無序、不可重複的需求,不能滿足。

    》》》》》集合:解決數組存儲資料方面的弊端。

2 單列集合架構結構Collection

java學習筆記(11)——集合詳解java學習筆記(11)——集合
java學習筆記(11)——集合詳解java學習筆記(11)——集合

2.1 Collection接口常用方法

  • add(Object e):将元素e添加到集合coll中
  • size():擷取添加的元素的個數
  • addAll(Collection coll1):将coll1集合中的元素添加到目前的集合中
  • clear():清空集合元素
  • isEmpty():判斷目前集合是否為空
public void test1(){
    Collection coll = new ArrayList();

    //add(Object e):将元素e添加到集合coll中
    coll.add("AA");
    coll.add("BB");
    coll.add(123);//自動裝箱
    coll.add(new Date());

    //size():擷取添加的元素的個數
    System.out.println(coll.size());//4

    //addAll(Collection coll1):将coll1集合中的元素添加到目前的集合中
    Collection coll1 = new ArrayList();
    coll1.add(456);
    coll1.add("CC");
    coll.addAll(coll1);

    System.out.println(coll.size());//6
    System.out.println(coll);

    //clear():清空集合元素
    coll.clear();

    //isEmpty():判斷目前集合是否為空
    System.out.println(coll.isEmpty());

}
           
  • contains(Object obj):判斷目前集合中是否包含obj
  • containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在于目前集合中
  • remove(Object obj):從目前集合中移除obj元素。
  • removeAll(Collection coll1):差集:從目前集合中移除coll1中所有的元素
  • retainAll(Collection coll1):交集:擷取目前集合和coll1集合的交集,并傳回給目前集合
  • equals(Object obj):要想傳回true,需要目前集合和形參集合的元素都相同。
  • hashCode():傳回目前對象的哈希值
  • 集合 —>數組:toArray()
  • 數組 —>集合:調用Arrays類的靜态方法asList()
public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
//        Person p = new Person("Jerry",20);
//        coll.add(p);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);
        //1.contains(Object obj):判斷目前集合中是否包含obj
        //我們在判斷時會調用obj對象所在類的equals()。
        boolean contains = coll.contains(123);
        System.out.println(contains);//true
        System.out.println(coll.contains(new String("Tom")));//true
//        System.out.println(coll.contains(p));//true
        System.out.println(coll.contains(new Person("Jerry",20)));//false -->true

        //2.containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在于目前集合中。
        Collection coll1 = Arrays.asList(123,4567);
        System.out.println(coll.containsAll(coll1));
    }

    @Test
    public void test2(){
        //3.remove(Object obj):從目前集合中移除obj元素。
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        coll.remove(1234);
        System.out.println(coll);

        coll.remove(new Person("Jerry",20));
        System.out.println(coll);

        //4. removeAll(Collection coll1):差集:從目前集合中移除coll1中所有的元素。
        Collection coll1 = Arrays.asList(123,456);
        coll.removeAll(coll1);
        System.out.println(coll);


    }

    @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //5.retainAll(Collection coll1):交集:擷取目前集合和coll1集合的交集,并傳回給目前集合
//        Collection coll1 = Arrays.asList(123,456,789);
//        coll.retainAll(coll1);
//        System.out.println(coll);

        //6.equals(Object obj):要想傳回true,需要目前集合和形參集合的元素都相同。
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add(123);
        coll1.add(new Person("Jerry",20));
        coll1.add(new String("Tom"));
        coll1.add(false);

        System.out.println(coll.equals(coll1));


    }

    @Test
    public void test4(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //7.hashCode():傳回目前對象的哈希值
        System.out.println(coll.hashCode());

        //8.集合 --->數組:toArray()
        Object[] arr = coll.toArray();
        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }

        //拓展:數組 --->集合:調用Arrays類的靜态方法asList()
        List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(list);

        List arr1 = Arrays.asList(new int[]{123, 456});
        System.out.println(arr1.size());//1

        List arr2 = Arrays.asList(new Integer[]{123, 456});
        System.out.println(arr2.size());//2

        //9.iterator():傳回Iterator接口的執行個體,用于周遊集合元素。放在IteratorTest.java中測試

    }

           

2.2Iterator 和 foreach循環

  • 作用:周遊Collection
  • 實作:

2.2.1 lterator :

  1. 内部的方法:hasNext() 和 next()
  2. 集合對象每次調用iterator()方法都得到一個全新的疊代器對象,預設遊标都在集合的第一個元素之前。
  3. 内部定義了remove(),可以在周遊的時候,删除集合中的元素。此方法不同于集合直接調用remove()
public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        Iterator iterator = coll.iterator();
        //方式一:
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        //報異常:NoSuchElementException
//        System.out.println(iterator.next());

        //方式二:不推薦
//        for(int i = 0;i < coll.size();i++){
//            System.out.println(iterator.next());
//        }

        //方式三:推薦
        hasNext():判斷是否還有下一個元素
        while(iterator.hasNext()){
            //next():①指針下移 ②将下移以後集合位置上的元素傳回
            System.out.println(iterator.next());
        }

    }

    @Test
    public void test2(){

        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //錯誤方式一:
//        Iterator iterator = coll.iterator();
//        while((iterator.next()) != null){
//            System.out.println(iterator.next());
//        }

        //錯誤方式二:
        //集合對象每次調用iterator()方法都得到一個全新的疊代器對象,預設遊标都在集合的第一個元素之前。
        while (coll.iterator().hasNext()){
            System.out.println(coll.iterator().next());
        }


    }

    //測試Iterator中的remove()
    //如果還未調用next()或在上一次調用 next 方法之後已經調用了 remove 方法,
    // 再調用remove都會報IllegalStateException。
    @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //删除集合中"Tom"
        Iterator iterator = coll.iterator();
        while (iterator.hasNext()){
//            iterator.remove();
            Object obj = iterator.next();
            if("Tom".equals(obj)){
                iterator.remove();
//                iterator.remove();
            }

        }
        //周遊集合
        iterator = coll.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
           

2.2.2 foreach循環

public void test1(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add(456);
    coll.add(new Person("Jerry",20));
    coll.add(new String("Tom"));
    coll.add(false);

    //for(集合元素的類型 局部變量 : 集合對象)
    //内部仍然調用了疊代器。
    for(Object obj : coll){
        System.out.println(obj);
    }
}

@Test
public void test2(){
    int[] arr = new int[]{1,2,3,4,5,6};
    //for(數組元素的類型 局部變量 : 數組對象)
    for(int i : arr){
        System.out.println(i);
    }
}
           

2.3 List接口

java學習筆記(11)——集合詳解java學習筆記(11)——集合
  • 存儲的資料特點: 存儲序的、可重複的資料。

2.3.1 常用方法

  • 增:add(Object obj)
  • 删:remove(int index) / remove(Object obj)
  • 改:set(int index, Object ele)
  • 查:get(int index)
  • 插:add(int index, Object ele)
  • 長度:size()
  • 周遊:① Iterator疊代器方式② 增強for循環③ 普通的循環

2.3.2 ArrayList的源碼分析:

jdk 7情況下:

  • ArrayList list = new ArrayList();//底層建立了長度是10的Object[]數組elementData

    list.add(11);//如果此次的添加導緻底層elementData數組容量不夠,則擴容。

    預設情況下,擴容為原來的容量的1.5倍,同時需要将原有數組中的資料複制到新的數組中。

jdk 8中ArrayList的變化:

  • ArrayList list = new ArrayList();//底層Object[] elementData初始化為{}.并沒建立長度為10的數組

    list.add(123);//第一次調用add()時,底層才建立了長度10的數組,并将資料123添加到

    後續的添加和擴容操作與jdk 7 無異。

jdk7中的ArrayList的對象的建立類似于單例的餓漢式,而jdk8中的ArrayList的對象的建立類似于單例的懶漢式,延遲了數組的建立,節省記憶體。

2.3.3 LinkedList的源碼分析:

LinkedList list = new LinkedList(); 内部聲明了Node類型的first和last屬性,預設值為null

list.add(123);//将123封裝到Node中,建立了Node對象。

其中,Node定義為:展現了LinkedList的雙向連結清單的說法

private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;

            Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
            }
        }
           

2.3.3 Vector的源碼分析:

jdk7和jdk8中通過Vector()構造器建立對象時,底層都建立了長度為10的數組。

在擴容方面,預設擴容為原來的數組長度的2倍。

2.3.4 Arraylist 與 LinkedList 差別?

  1. 是否保證線程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保證線程安全;
  2. 底層資料結構: Arraylist 底層使⽤的是 Object 數組; LinkedList 底層使⽤的是 雙向鍊

    表 資料結構

  3. 插⼊和删除是否受元素位置的影響: ① ArrayList 采⽤數組存儲,是以插⼊和删除元素的

    時間複雜度受元素位置的影響。 ⽐如:執⾏ add(E e) ⽅法的時候, ArrayList 會預設在将

    指定的元素追加到此清單的末尾,這種情況時間複雜度就是 O(1)。但是如果要在指定位置 i

    插⼊和删除元素的話( add(int index, E element) )時間複雜度就為 O(n-i)。因為在進⾏上述操作的時候集合中第 i 和第 i 個元素之後的(n-i)個元素都要執⾏向後位/向前移⼀位的操作。 ②

    LinkedList 采⽤連結清單存儲,是以對于 add(E e) ⽅法的插⼊,删除元素時間複雜度不受元素

    位置的影響,近似 O(1),如果是要在指定位置 i 插⼊和删除元素的話( (add(int index, E

    element) ) 時間複雜度近似為 o(n)) 因為需要先移動到指定位置再插⼊。

  4. 是否⽀持快速随機通路: LinkedList 不⽀持⾼效的随機元素通路,⽽ ArrayList ⽀持。快

    速随機通路就是通過元素的序号快速擷取元素對象(對應于 get(int index) ⽅法)。

  5. 記憶體空間占⽤: ArrayList 的空 間浪費主要展現在在 list 清單的結尾會預留⼀定的容量空

    間,⽽ LinkedList 的空間花費則展現在它的每⼀個元素都需要消耗⽐ ArrayList 更多的空間

    (因為要存放直接後繼和直接前驅以及資料)。

2.4 Set接口

存儲的資料特點:無序的、不可重複的元素

java學習筆記(11)——集合詳解java學習筆記(11)——集合

2.4.1 HashSet

1.無序性: 不等于随機性。存儲的資料在底層數組中并非照數組索引的順序添加,而是根據資料的哈希值決定的。

2.不可重複性: 保證添加的元素照equals()判斷時,不能傳回true.即:相同的元素隻能添加一個。

3.底層原理

  • 我們向HashSet中添加元素a,首先調用元素a所在類的hashCode()方法,計算元素a的哈希值,此哈希值接着通過 (n - 1) & hash(這⾥的 n 指的是數組的⻓度) 計算出在HashSet底層數組中的存放位置(即為:索引位置,判斷

    數組此位置上是否已經元素:

  • 如果此位置上沒其他元素,則元素a添加成功。 —>情況1
  • 如果此位置上其他元素b(或以連結清單形式存在的多個元素,則比較元素a與元素b的hash值:

    如果hash值不相同,則元素a添加成功。—>情況2

  • 如果hash值相同,進而需要調用元素a所在類的equals()方法:

    equals()傳回true,元素a添加失敗

    equals()傳回false,則元素a添加成功。—>情況3

  • 對于添加成功的情況2和情況3而言:元素a 與已經存在指定索引位置上資料以連結清單的方式存儲。

    jdk 7 :元素a放到數組中,指向原來的元素。

    jdk 8 :原來的元素在數組中,指向元素a

    總結:七上八下

  • HashSet底層:數組+連結清單的結構。(前提:jdk7)

4.要求:向Set(主要指:HashSet、LinkedHashSet)中添加的資料,其所在的類一定要重寫hashCode()和equals() 要求:重寫的hashCode()和equals()盡可能保持一緻性:相等的對象必須具有相等的散列碼

2.4.2 TreeSet的使用

  1. 向TreeSet中添加的資料,要求是相同類的對象。
  2. 兩種排序方式:自然排序(實作Comparable接口 和 定制排序(Comparator)

3 雙列集合架構:Map

java學習筆記(11)——集合詳解java學習筆記(11)——集合

Map中的key:無序的、不可重複的,使用Set存儲所的key —> key所在的類要重寫equals()和hashCode() (以HashMap為例)

Map中的value:無序的、可重複的,使用Collection存儲所的value —>value所在的類要重寫equals()

一個鍵值對:key-value構成了一個Entry對象。

Map中的entry:無序的、不可重複的,使用Set存儲所的entry

常用方法

  • 添加:put(Object key,Object value)
  • 删除:remove(Object key)
  • 修改:put(Object key,Object value)
  • 查詢:get(Object key)
  • 長度:size()
  • 周遊:keySet() / values() / entrySet()

3.1 HashMap

作為Map的主要實作類;線程不安全的,效率高;存儲null的key和value

HashMap在jdk7中實作原理:

  • HashMap map = new HashMap():在執行個體化以後,底層建立了長度是16的一維數組Entry[] table。
  • map.put(key1,value1):首先,調用key1所在類的hashCode()計算key1哈希值,此哈希值經過某種算法計算以後,得到在Entry數組中的存放位置。 如果此位置上的資料為空,此時的key1-value1添加成功。 ----情況1
  • 如果此位置上的資料不為空,(意味着此位置上存在一個或多個資料(以連結清單形式存在)),比較key1和已經存在的一個或多個資料的哈希值: 如果key1的哈希值與已經存在的資料的哈希值都不相同,此時key1-value1添加成功。----情況2
  • 如果key1的哈希值和已經存在的某一個資料(key2-value2)的哈希值相同,繼續比較:調用key1所在類的equals(key2)方法,比較: 如果equals()傳回false:此時key1-value1添加成功。如果equals()傳回true:使用value1替換value2。----情況3
  • 補充:關于情況2和情況3:此時key1-value1和原來的資料以連結清單的方式存儲。
  • 在不斷的添加過程中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容。預設的擴容方式:擴容為原來容量的2倍,并将原的資料複制過來。

HashMap在jdk8中相較于jdk7在底層實作方面的不同:

  1. new HashMap():底層沒建立一個長度為16的數組
  2. jdk 8底層的數組是:Node[],而非Entry[]
  3. 首次調用put()方法時,底層建立長度為16的數組
  4. jdk7底層結構隻:數組+連結清單。jdk8中底層結構:數組+連結清單+紅黑樹。
  5. 形成連結清單時,七上八下(jdk7:新的元素指向舊的元素。jdk8:舊的元素指向新的元素)
  6. 當數組的某一個索引位置上的元素以連結清單形式存在的資料個數 > 8 且目前數組的長度 > 64時,此時此索引位置上的所資料改為使用紅黑樹存儲。

HashMap底層典型屬性的屬性的說明:

  1. DEFAULT_INITIAL_CAPACITY : HashMap的預設容量,16
  2. DEFAULT_LOAD_FACTOR:HashMap的預設加載因子:0.75
  3. threshold:擴容的臨界值,=容量*填充因子:16 * 0.75 => 12
  4. TREEIFY_THRESHOLD:Bucket中連結清單長度大于該預設值,轉化為紅黑樹:8
  5. MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64

3.2 LinkedHashMap

  • LinkedHashMap底層使用的結構與HashMap相同,因為LinkedHashMap繼承于HashMap.

    差別就在于:LinkedHashMap内部提供了Entry,替換HashMap中的Node.

java學習筆記(11)——集合詳解java學習筆記(11)——集合

TreeMap的使用

  • 向TreeMap中添加key-value,要求key必須是由同一個類建立的對象
  • 因為要照key進行排序:自然排序 、定制排序

4 Collection 和 Collections的差別?

  • java.util.Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操作的通用接口方法。Collection接口在Java 類庫中有很多具體的實作。Collection接口的意義是為各種具體的集合提供了最大化的統一操作方式,其直接繼承接口有List與Set。
  • Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜态方法,用于對集合中元素進行排序、搜尋以及線程安全等各種操作。

    - 常用方法:

    reverse(List):反轉 List 中元素的順序

    shuffle(List):對 List 集合元素進行随機排序

    sort(List):根據元素的自然順序對指定 List 集合元素升序排序

    sort(List,Comparator):根據指定的 Comparator 産生的順序對 List 集合元素進行排序

    swap(List,int, int):将指定 list 集合中的 i 處元素和 j 處元素進行交換

5 常見面試題

5.1 HashMap 和 Hashtable 的差別

  1. 線程是否安全: HashMap 是⾮線程安全的, HashTable 是線程安全的,因為 HashTable 内

    部的⽅法基本都經過 synchronized 修飾。(如果你要保證線程安全的話就使⽤

    ConcurrentHashMap 吧!);

  2. 效率: 因為線程安全的問題, HashMap 要⽐ HashTable 效率⾼⼀點。另外, HashTable

    基本被淘汰,不要在代碼中使⽤它;

  3. 對 Null key 和 Null value 的⽀持: HashMap 可以存儲 null 的 key 和 value,但 null 作為

    鍵隻能有⼀個,null 作為值可以有多個;HashTable 不允許有 null 鍵和 null 值,否則會抛出

    NullPointerException 。

  4. 初始容量⼤⼩和每次擴充容量⼤⼩的不同 : ① 建立時如果不指定容量初始值, Hashtable

    預設的初始⼤⼩為 11,之後每次擴充,容量變為原來的 2n+1。 HashMap 預設的初始化⼤

    ⼩為 16。之後每次擴充,容量變為原來的 2 倍。② 建立時如果給定了容量初始值,那麼

    Hashtable 會直接使⽤你給定的⼤⼩,⽽ HashMap 會将其擴充為 2 的幂次⽅⼤⼩

    ( HashMap 中的 tableSizeFor() ⽅法保證,下⾯給出了源代碼)。也就是說 HashMap 總

    是使⽤ 2 的幂作為哈希表的⼤⼩,後⾯會介紹到為什麼是 2 的幂次⽅。

  5. 底層資料結構: JDK1.8 以後的 HashMap 在解決哈希沖突時有了⼤的變化,當連結清單⻓度

    ⼤于門檻值(預設為 8)(将連結清單轉換成紅⿊樹前會判斷,如果目前數組的⻓度⼩于 64,那麼

    會選擇先進⾏數組擴容,⽽不是轉換為紅⿊樹)時,将連結清單轉化為紅⿊樹,以減少搜尋時

    間。Hashtable 沒有這樣的機制。

5.2 HashMap 和 HashSet差別

HashSet 底層就是基于 HashMap 實作的

java學習筆記(11)——集合詳解java學習筆記(11)——集合

5.3 HashMap 的⻓度為什麼是2的幂次⽅

采⽤%取餘的操作來實作。但是,重點來了:“取餘(%)操作中如果除數是2的幂次則等價于與其除數減⼀的與(&)操作(也就是說 **hash%length==hash&(length-1)**的前提是 length 是2的 n 次⽅;)。” 并且 采⽤⼆進制位操作 &,相對于%能夠提⾼運算效率,這就解釋了 HashMap 的⻓度為什麼是2的幂次⽅。

5.4 ConcurrentHashMap

JDK1.7

  • ⾸先将資料分為⼀段⼀段的存儲,然後給每⼀段資料配⼀把鎖,當⼀個線程占⽤鎖通路其中⼀個段資料時,其他段的資料也能被其他線程通路。ConcurrentHashMap 是由 Segment 數組結構和 HashEntry 數組結構組成。**Segment 實作了 ReentrantLock ,**是以 Segment 是⼀種可重⼊鎖,扮演鎖的⻆⾊。 HashEntry ⽤于存儲鍵值對資料。
  • ⼀個 ConcurrentHashMap ⾥包含⼀個 Segment 數組。 Segment 的結構和 HashMap 類似,是⼀種數組和連結清單結構,⼀個 Segment 包含⼀個 HashEntry 數組,每個 HashEntry 是⼀個連結清單結構的元素,每個 Segment 守護着⼀個 HashEntry 數組⾥的元素,當對 HashEntry 數組的資料進⾏修改時,必須⾸先獲得對應的 Segment 的鎖。

JDK1.8

  • ConcurrentHashMap 取消了 Segment 分段鎖,采⽤ CAS 和 synchronized 來保證并發安全。資料結構跟 HashMap1.8 的結構類似,數組+連結清單/紅⿊⼆叉樹。
  • Java 8 在連結清單⻓度超過⼀定門檻值(8)時将連結清單(尋址時間複雜度為 O(N))轉換為紅⿊樹(尋址時間複雜度為 O(log(N)))synchronized 隻鎖定目前連結清單或紅⿊⼆叉樹的⾸節點,這樣隻要 hash 不沖突,就不會産⽣并發,效率⼜提升 N 倍

5.5 ConcurrentHashMap 和 Hashtable 的差別

ConcurrentHashMap 和 Hashtable 的差別主要展現在實作線程安全的⽅式上不同。

  • 底層資料結構: JDK1.7 的 ConcurrentHashMap 底層采⽤ 分段的數組+連結清單 實作,JDK1.8

    采⽤的資料結構跟 HashMap1.8 的結構⼀樣,數組+連結清單/紅⿊⼆叉樹。 Hashtable 和

    JDK1.8 之前的 HashMap 的底層資料結構類似都是采⽤ 數組+連結清單 的形式,數組是

    HashMap 的主體,連結清單則是主要為了解決哈希沖突⽽存在的;

  • **實作線程安全的⽅式(重要): ① 在 JDK1.7 的時候, ConcurrentHashMap (分段鎖)

    對整個桶數組進⾏了分割分段( Segment ),每⼀把鎖隻鎖容器其中⼀部分資料,多線程通路

    容器⾥不同資料段的資料,就不會存在鎖競争,提⾼并發通路率。 到了 JDK1.8 的時候已經

    摒棄了 Segment 的概念,⽽是直接⽤ Node 數組+連結清單+紅⿊樹的資料結構來實作,并發

    控制使⽤ synchronized 和 CAS 來操作。(JDK1.6 以後 對 synchronized 鎖做了很多優

    化) 整個看起來就像是優化過且線程安全的 HashMap ,雖然在 JDK1.8 中還能看到

    Segment 的資料結構,但是已經簡化了屬性,隻是為了相容舊版本;② Hashtable (同⼀把

    鎖) :使⽤ synchronized 來保證線程安全,效率⾮常低下。**當⼀個線程通路同步⽅法時,其

    他線程也通路同步⽅法,可能會進⼊阻塞或輪詢狀态,如使⽤ put 添加元素,另⼀個線程不

    能使⽤ put 添加元素,也不能使⽤ get,競争會越來越激烈效率越低。