java 集合hashmap hashset arraylist 詳解以及常見面試題


   今天復習了一下自認為java 中很重要的一部分集合,這篇文章主要從底層源碼進行分析這幾種集合的區別與聯系,他們的用法不多講,用法不難;大多數東西我也是從各位大神的博客上或者書上扒下來的,小菜鳥在復習,寫下來主要是一:是為了想留下點東西 二:我發現在寫的過程中我可以學到很多東西;

關於java 集合的內容之前在阿里電面的時候問到的我第二個問題就是關於HashTable 線程安全的問題,問題不難但是深度問起來還是挺難回答的。接下來我根據我自己復習的情況簡單寫一篇文章供大家學習,希望多多指教;

    這是一張Java集合的總框架圖很清晰明了  圖片來源(http://blog.sina.com.cn/s/blog_51721d970100a37a.html)

 迭代器:

     在講集合之前我又不得不先看一眼迭代器   collection 和map  都屬於迭代器模式的的一種;

     迭代器(iterator)模式    :迭代器(Iterator)模式,又叫做游標(Cursor)模式。GOF給出的定義為:提供一種方法訪問一個容器(container)對象中各個元素,而   又不需暴露該對象的內部細節

  在講集合之前我又不得不先看一眼迭代器   collection 和map  都屬於迭代器的一種;

我先根據我自己的理解給大家簡單的講解一下    下邊橙色的是實現類 也是我們最常使用的一種;草綠色的是抽象類上邊藍色的是接口;


一:先講map, Map沒有繼承Collection接口,Map提供key到value的映射。一個Map中不能包含相同的key,每個key只能映射一個value。Map的內容可以被當作一組key集合,一組value集合,或者一組key-value映射。  

 

       面試中常問的問題是HashMap 和HashTable 的區別與聯系    在講他們之前   HashCode 需要簡單的了解一下   hashCode方法,這個方法我們平時通常是用不到的,它是為哈希家族的集合類框架(HashMap、HashSet、HashTable)提供服務,hashCode 的常規協定是:

      關於HashMap和HashTable  是面試中常問的java  經典問題;

      HashMap 幾乎等同於HashTable  但是因為HashTable  使用同步鎖   所以線程安全;   判斷值得存在性如圖1.1

  HashMap: 1.線程不安全(因為沒有使用同步鎖(synchronized)),

        2.允許value  為null  ,

        3.HashMap  底層是通過Entry[]  每次新建一個HashMap 就相當於新建一個Entry[];圖1.2實現原理圖

                     4.調用put方法是首先通過獲取  key的Hashcode 來保證key 的唯一性

        5.get,put 方法都通過數組鏈表指針移動等方式 利用hashcode   實現;不詳細講   

  HashTable:線程安全(使用synchronized),不允許value  為 null;    其他的和HashMap  類似不多講;

 

   

 

 

          

     

                 圖1.1                                                                    圖1.2

 

 

 


 

 

 

 

二 :關於colletion Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。

    List  and  set  :

   1、List,Set都是繼承自Collection接口

   2、List特點:元素有放入順序,元素可重復 ,

      Set特點:元素無放入順序,元素不可重復(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決    定的,其位置其實是固定的)

   3、List接口有三個實現類:LinkedList,ArrayList,Vector ,Set接口有兩個實現類:HashSet(底層由HashMap實現),LinkedHashSet

   List:

 

      1. vector 是同步的所以線程安全  提供的方法和arraylist 類似不多講

      2.關於ArrayList 和 Linkist 類(自認為是重點 )   多講一點  線程不安全

        ArrayList 優點:  提供了可增長數組的實現;對查詢的調用花費常數的時間;

        缺點:新項的插入和現有項的刪除耗費時間較長  線程不安全

        而Linklist 與Array List 正好相反   它不容易做索引所以對於查詢  耗費相對多一些但是對於add and  remove  方法開銷缺很小;總而言之各有優缺點;(面試常    問

        其導致的原因就是   arraylist 底層用數組的形式實現的list  而 LinkList 底層用底層用雙向循環鏈表 實現的List

 

    接下來就是set 方法了;

   set:

       HashSet  =>  1.hashset 底層是用hashmap  實現的(圖 1—1 ) 

           2.hashset 的add方法實際上是將需要添加的對象看做底層維護的map的key值儲存的,而map  的value值都是同一個值;(圖 1—2)

            (講到這大家應該明白為什么hashset中不能儲存相同的值了,因為map中鍵值對的形式,鍵值不能相同,所以。。。。)

                           3.hashset的remove和clear實際上都是調用了map  的方法;更加充分說明了hashset底層以hashmap  為基礎;(圖 1—3)

                       圖 1—1                                               圖1—2                                                                      圖1-3

 

 

 

 

 

 

     


 

 

 下面是關於集合的幾種常見面試題  題目來自於百度經驗

 

  1. 集合類特性(幾個常用類的區別)

     

    ArrayList: 元素單個,效率高,多用於查詢  Vector: 元素單個,線程安全,多用於查詢  LinkedList: 元素單個,多用於插入和刪除  HashMap: 元素成對,元素可為空  HashTable: 元素成對,線程安全,元素不可為空

    WeakHashMap: 是一種改進的HashMap,它對key實行“弱引用”,如果一個key不再被外部所引用,那么該key可以被GC回收

  2. Iterator 是什么

    一些集合類提供了內容遍歷的功能,通過java.util.Iterator接口。這些接口允許遍歷對象的集合。依次操作每個元素對象。當使用Iterators時,在獲得Iterator的時候包含一個集合快照。通常在遍歷一個Iterator的時候不建議修改集合本省。

     

    Iterator與ListIterator有什么區別

     

    1. Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。  

    2. Iterator只能正向遍歷集合,適用於獲取移除元素。ListIerator繼承Iterator,可以雙向列表的遍歷,同樣支持元素的修改。比如:增加元素,替換元素,獲取前一個和后一個元素的索引,等等。

  3. Collection 和 Collections的區別

     

    Collection是集合類的上級接口,繼承與他的接口主要有Set 和List.

    Collections是針對集合類的一個幫助類,他提供一系列靜態方法實現對各種集合的搜索、排序、線程安全化等操作

    List、Map、Set三個接口,存取元素時,各有什么特點

     

    List  以特定次序來持有元素,可有重復元素;

    Set  無法擁有重復元素,內部排序(無序);

    Map 保存key-value值,value可多值。

     

    Java集合面試題:[1]集合面試題、Java集合
  4. HashMap和Hashtable的區別

    都屬於Map接口的類,實現了將惟一鍵映射到特定的值上。   

    一.歷史原因:

    Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現  

    二.同步性:

    Hashtable是線程安全的,也就是說是同步的,而HashMap是線程序不安全的,不是同步的  

    三.值:

     HashMap 類沒有分類或者排序。它允許一個 null 鍵和多個 null 值。 

     Hashtable 類似於 HashMap,但是不允許 null 鍵和 null 值。

    四.效率:

    Hashtable 比 HashMap 慢,因為它是同步的。

     

    怎樣使HashMap同步

     

    HashMap可以通過Map m = Collections.synchronizedMap(hashMap)來達到同步的效果。

  5. ArrayList和Vector的區別

     

    ArrayList與Vector主要從二方面來說.  

    一.同步性:

       Vector是線程安全的,也就是說是同步的,而ArrayList是線程序不安全的,不是同步的。

    二.操作:

        由於Vector支持多線程操作,所以在性能上就比不上ArrayList了。

    三.數據增長:

           ArrayList和Vector都有一個初始的容量大小,當存儲進去它們里面的元素個數超出容量的時候,就需要增加ArrayList和Vector的存儲空間,每次增加存儲空間的時候不是只增加一個存儲單元,是增加多個存儲單元。

           Vector默認增加原來的一倍,ArrayList默認增加原來的0.5倍。

           Vector可以由我們自己來設置增長的大小,ArrayList沒有提供相關的方法。

    LinkedList與ArrayList有什么區別

     

    兩者都實現的是List接口,不同之處在於:

    (1)、ArrayList是基於動態數組實現的,LinkedList是基於鏈表的數據結構。

    (2)、get訪問List內部任意元素時,ArrayList的性能要比LinkedList性能好。LinkedList中的get方法是要按照順序從列表的一端開始檢查,直到另一端

    (3)、對於新增和刪除操作LinkedList要強於ArrayList,因為ArrayList要移動數據

    附加:

           LinkedList實現了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。

           注意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:

    List list = Collections.synchronizedList(new LinkedList(…));

  6. 數組(Array)和列表集合(ArrayList)有什么區別

    下面列出了Array和ArrayList的不同點: 

            Array可以包含基本類型和對象類型,ArrayList只能包含對象類型。

            Array大小是固定的,ArrayList的大小是動態變化的。 

            ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()  等等。

           對於基本類型數據,集合使用自動裝箱來減少編碼工作量。但是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。

    Enumeration接口和Iterator接口的區別有哪些

     

           Enumeration速度是Iterator的2倍,同時占用更少的內存。但是,Iterator遠遠比Enumeration安全,因為其他線程不能夠修改正在被iterator遍歷的集合里面的對象。同時,Iterator允許調用者刪除底層集合里面的元素,這對Enumeration來說是不可能的。

  7. HashSet和TreeSet有什么區別 

     

    HashSet有以下特點:

    A. 無序(不能保證元素的排列順序,順序有可能發生變化)B. 不同步C. 允許空值(集合元素可以是null,可以放入多個null,但會自動覆蓋)

    當向HashSet結合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然后根據 hashCode值來決定該對象在HashSet中存儲位置。簡單的說,HashSet集合判斷兩個元素相等的標准是兩個對象通過equals方法比較相等,並且兩個對象的hashCode()方法返回值相 等注意,如果要把一個對象放入HashSet中,重寫該對象對應類的equals方法,也應該重寫其hashCode()方法。其規則是如果兩個對象通過equals方法比較返回true時,其hashCode也應該相同。另外,對象中用作equals比較標准的屬性,都應該用來計算 hashCode的值。

     

    TreeSet有以下特點:

    A. 有序

          1. TreeSet是由一個樹形的結構來實現的(數據結構是二叉樹),它里面元            素是有序的

          2.TreeSet是SortedSet接口的唯一實現類,TreeSet可以確保集合元素處於           排序狀 態,支持兩種排序方式,自然排序和定制排序。其中自然排序為             默認的排序方式;定制排序,TreeSet中的對象元素需要實Comparable             接口

        TreeSet類中跟HashSet類一樣也沒有get()方法來獲取列表中的元素,所以      也只能通過迭代器方法來獲取

        二叉樹:

              

    B. 不允許空值

     

          1. HashSet是通過HashMap實現的,TreeSet是通過TreeMap實現的,只不過            Set用的只是Map的key

          2. Map的key和Set都有一個共同的特性就是集合的唯一性.TreeMap更是              多 了一個有序性.

          3. TreeSet類中跟HashSet類一樣也沒有get()方法來獲取列表中的元素,所            以也只能通過迭代器方法來獲取

          4. HashSet是基於hash算法實現的,性能優於TreeSet,通常使用                        HashSet。在我們需要對其中元素排序的時候才使用TreeSet。

    Java集合面試題:[1]集合面試題、Java集合
  8. HashMap,LinkedMap,TreeMap的區別

     

    HashMap,LinkedHashMap,TreeMap都屬於Map。

    LinkedHashMap是HashMap的子類。

    Map 主要用於存儲鍵(key)值(value)對,根據鍵得到值,因此鍵不允許鍵重復,但允許值重復

     

    1.HashMap的內部結構是一個數組,線性順序存儲,二次結構使用線性的單鏈表。使用key的hashCode做二次hash之后,再截取小於數組長度的值為索引值。key可以為null,存在索引為0的位置上。由於使用了數組,所以有一個負載因子loadFactor的概念(臨界閾值threshold)和resize。resize比較耗時,沖突時鏈式遍歷查找也比較耗時,所以選定一個合適的初始容易比較重要。存取性能都較高。迭代遍歷時一維使用數組,二維使用鏈表。

              HashMap            是一個最常用的Map,它根據鍵的HashCode 值存儲數據,根據鍵可以直              接獲取它的值,具有很快的訪問速度。HashMap最多只允許一條記錄的            鍵為Null;允許多條記錄的值為 Null;HashMap不支持線程的同步,即任             一 時刻可以有多個線程同時寫HashMap;可能會導致數據的不一致。如             果需要同步,可以用 Collections的synchronizedMap方法使HashMap           具有同步的能力。

    2.LinkedHashMap是HashMap的子類。內部結構是一個數組,線性順序存儲,二次結構使用線性的單鏈表,但同時內部維護了一個雙向循環鏈表,可以保持順序。access-order=false默認為使用新增存儲順序,access-order=true則指定使用讀取和訪問順序。removeEldestEntry=false(當指定為true時,就是實現LRU算法的緩存容器,當然要指定淘汰時的使用頻率和容量上限,其實是一個最近最少使用-->最近使用access-order=true/最新存儲access-order=false)。存取性能較HashMap差-些,但相差不大。header.after為尾方向,header.before為首方向。迭代遍歷時entrySet().iterator()跟HashMap一樣(有點困惑,為什么不按線性順序進行迭代,只能重寫entrySet(),keySet()和values()方法)。適用於有緩存設計需求的情況(需繼承)。 3.TreeMap的內部結構是一棵紅黑樹(又叫排序數,是二叉樹的一種),使用鏈式存儲,可以指定比較器Comparator,key需實現Comparable接口。key不能為null。存結點性能稍差,因為需要調整樹結構;取結點用的是鏈表遍歷,但是屬於有序比較,性能中等。迭代遍歷時用的樹的中序遍歷,是一個有序序列。適用於有排序需求的情況。

     

     


    下面是我測試用的簡單的代碼  發給大家吧:

     /*ArrayList */
        public static void Array (){
            ArrayList<String> arrayList = new ArrayList();
            /*arrlist 添加的方法 */
            arrayList.add("你好");
            arrayList.add("我好");
            arrayList.add("他好");
            arrayList.add("大家好");
            arrayList.add("真的好");
            //查詢size
            int size = arrayList.size();
            //查詢特定的元素是否存在
            boolean has = arrayList.contains("a");
            //是否為空
            boolean isEmpty = arrayList.isEmpty();
            //根據下標刪除特定的元素
            arrayList.remove(1);
            //根據內容刪除特定元素
            arrayList.remove("a");
            //刪除全部元素
            arrayList.removeAll(arrayList);
        }
        public static void Link (){
            LinkedList<String> linkedList = new LinkedList();
            /*linklist 添加的方法 */
            linkedList.add("你好");
            linkedList.add("我好");
            linkedList.add("他好");
            linkedList.add("大家好");
            linkedList.add("真的好");
            //查詢size
            int size = linkedList.size();
            //查詢特定的元素是否存在
            boolean has = linkedList.contains("a");
            //是否為空
            boolean isEmpty = linkedList.isEmpty();
            //根據下標刪除特定的元素
            linkedList.remove(1);
            //根據內容刪除特定元素
            linkedList.remove("a");
            //刪除全部元素
            linkedList.removeAll(linkedList);
    
            /*  這是比arraylist 多提供的一些方法  不多解釋一看就懂*/
            linkedList.getFirst();
            linkedList.getLast();
            linkedList.removeFirst();
            linkedList.removeLast();
        }
       /*Treeset  以及利用構造器進行數據排序*/
    
       public static  void tree(){
    
           TreeSet  treeSet = new TreeSet();
           treeSet.add("A");
           treeSet.add("B");
           treeSet.add("D");
           treeSet.add("C");
    
           for(Iterator iterator= treeSet.iterator();iterator.hasNext();){
    
               System.out.println((String)iterator.next());
           }
       }
    
       /*HashSet */
        public static  void hash(){
    
            HashSet hashSet = new HashSet();
            hashSet.add("A");
            hashSet.add("B");
            hashSet.add("D");
            hashSet.add("C");
    
            /*利用迭代器的方法   排序輸出*/
            for(Iterator iterator= hashSet.iterator();iterator.hasNext();){
                System.out.println((String)iterator.next());
            }
        }
    
        /*Hash Map  */
    
        public static  void hashmap(){
    
            HashMap hashMap = new HashMap();
            hashMap.put("a","A");
            hashMap.put("b","A");
            hashMap.put("c","A");
            hashMap.put("d","A");
            hashMap.put("e",null);
            /*判斷某個值得存在性*/
            hashMap.containsKey("a");
            hashMap.containsValue("A");
    
            System.out.println(hashMap.get("e"));
        }
        /*HashTable*/
        public static  void hashtable(){
    
            Hashtable hashtable = new Hashtable();
            hashtable.put("a","A");
            hashtable.put("b","A");
            hashtable.put("c","A");
            hashtable.put("d","A");
            /*判斷某個值得存在性    */
            hashtable.containsKey("a");
            hashtable.containsValue("A");
            hashtable.contains("b");
    
            System.out.println(hashtable.get("e"));
    
    
        }
    
       public static void main(String[] atgs){
           tree();
           hashmap();
           hashtable();
       }

     

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM